diff --git a/application/admincpp/common.php b/application/admincpp/common.php new file mode 100644 index 0000000..6fd199e --- /dev/null +++ b/application/admincpp/common.php @@ -0,0 +1 @@ +redirect('login/index'); + return $this->error('您还未登录',url('Login/logoutJump')); + //Response::create(['status' => '0','msg'=> '还未登录,验证失败'], 'json')->send(); + exit; + }else{ + //登录信息超时 + $log_time=session('admincpp_user_login_time')-time(); + if($log_time < 0){ + Session::clear(); + //Response::create(['status' => '0','msg'=> '登录超时,需重新登陆'], 'json')->send(); + return $this->error('登录超时',url('Login/logoutJump')); + exit; + } + } + } + //简单的token生成函数 + /* protected function create_token() + { + $str = md5(uniqid(md5(microtime(true)), true)); //生成一个不会重复的字符串 + $str = sha1($str); //加密 + return $str; + } + //验证Token + public function checkToken($token) + { + $user = Db::name('user')->where('token', $token)->find(); + if (empty($user)) { + return ['status'=>0,'msg'=>'token错误,验证失败']; + }else{ + if (time() - $user['time_out'] > 0) { + return ['status'=>0,'msg'=>'token长时间未使用而过期,需重新登陆']; + } + return ['status'=>1,'msg'=>'成功!','data'=>$user]; + } + } + //通过分组id获取分组名称 + public function getGroupName($id){ + $res = Db::name('group')->field('group_name')->where('group_id',$id)->find(); + if($res){ + return $res['group_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'分组匹配不到']); + return ''; + } + } + //通过城市id获取城市名称 + public function getCityName($id){ + $res = Db::name('city')->field('city_name')->where('city_id',$id)->find(); + if($res){ + return $res['city_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'城市匹配不到']); + return ''; + } + } + //通过工厂id获取工厂名称 + public function getFirmName($id){ + $res = Db::name('firm')->field('firm_name')->where('firm_id',$id)->find(); + if($res){ + return $res['firm_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'工厂匹配不到']); + return ''; + } + } + //通过品牌id获取品牌名称 + public function getBrandName($id){ + $res = Db::name('brand')->field('brand_name')->where('brand_id',$id)->find(); + if($res){ + return $res['brand_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'品牌匹配不到']); + return ''; + } + } + //通过代理商id获取代理商名称 + public function getVenderName($id){ + $res = Db::name('vender')->field('vender_name')->where('vender_id',$id)->find(); + if($res){ + return $res['vender_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'代理商匹配不到']); + return ''; + } + } + //通过角色id获取角色名称 + public function getRoleName($id){ + $res = Db::name('role')->field('role_name')->where('role_id',$id)->find(); + if($res){ + return $res['role_name']; + }else{ + // return json_encode(['status'=>0,'msg'=>'角色匹配不到']); + return ''; + } + }*/ +} diff --git a/application/admincpp/config.php b/application/admincpp/config.php new file mode 100644 index 0000000..d36ae2b --- /dev/null +++ b/application/admincpp/config.php @@ -0,0 +1,5 @@ +view->fetch("dashboard/index"); + } + + public function getStatistics() + { + try { + $userCount = Db::name("wechat_user")->count(); + $todayUserCount = Db::name("wechat_user") + ->where("create_time", ">=", date("Y-m-d") . " 00:00:00") + ->count(); + $recordCount = Db::name("cpp_calculate_record")->count(); + $infoCount = Db::name("wechat_real_time_info")->count(); + $lastCalculate = Db::name("cpp_calculate_record") + ->order("create_time desc")->value("create_time"); + + return json([ + "status" => 1, + "data" => [ + "user_count" => $userCount, + "today_user_count" => $todayUserCount, + "total_calculate_count" => $recordCount, + "info_count" => $infoCount, + "last_calculate_time" => $lastCalculate ?: "暂无", + ] + ]); + } catch (\Exception $e) { + return json(["status" => 0, "msg" => $e->getMessage()]); + } + } +} diff --git a/application/admincpp/controller/Files.php b/application/admincpp/controller/Files.php new file mode 100644 index 0000000..04564e3 --- /dev/null +++ b/application/admincpp/controller/Files.php @@ -0,0 +1,118 @@ +"-1","error"=>"图片不能为空")); + exit; + } + $type = strtolower(substr(strrchr($name, '.'), 1)); //获取文件类型 + + if (!in_array($type, $typeArr)) { + return json(array("status"=>"-1","error"=>"请上传jpg,png,或jpeg类型的文件!"));//mov,webm + exit; + } + if ($size > (2 * 1024 * 1024)) { + return json(array("status"=>"-1","error"=>"文件大小已超过2MB!")); + exit; + } + + /* if($_GET['type'] == 'cx'){ + $imageType = 'cx'; + }*/ + $imageType=''; + $pic_name = $imageType.time() . rand(10000, 99999) . "." . $type;//图片名称 + $pic_url =$path . $pic_name;//上传后图片路径+名称 + + if (move_uploaded_file($name_tmp, $pic_url)) { //临时文件转移到目标文件夹 + /* $_POST['name'] = $pic_name; + $_POST['topic'] = $imageType; + $_POST['albumPath'] = $pic_url;*/ + return json(array("status"=>"0","file_name"=>$pic_name,"file_path"=>$request->domain().'/'.$pic_url)); + die(); + } else { + return json(array("status"=>"-1","error"=>"上传有误,请检查服务器配置!")); + die(); + } + }else{ + return json(array("status"=>"-1","error"=>"系统错误!")); + die(); + } + } + /** + *上传视频 + */ + public function uploadVideo(Request $request){ + $typeArr = array("mp4","wkv");//官方推荐 mp4 + $path = "uploadnv/" . date('Y/m/d/');//上传路径 + if(!file_exists($path)){ + //创建文件 + mkdir($path,0777,true); + } + // if ($_FILES) { + $name = $_FILES['file']['name']; + $size = $_FILES['file']['size']; + $name_tmp = $_FILES['file']['tmp_name']; + if (empty($name)) { + // return json_encode(['status'=>0,'msg'=>'查询失败']); + echo json_encode(array("error"=>"您还未选择文件")); + exit; + } + $type = strtolower(substr(strrchr($name, '.'), 1)); //获取文件类型 + + if (!in_array($type, $typeArr)) { + echo json_encode(array("error"=>"请上传mp4,wkv类型的文件!"));//mov,webm + exit; + } + if ($size > (15 * 1024 * 1024)) { + echo json_encode(array("error"=>"文件大小已超过30MB!")); + exit; + } + + /* if($_GET['type'] == 'cx'){ + $imageType = 'cx'; + }*/ + $imageType=''; + $pic_name = $imageType.time() . rand(10000, 99999) . "." . $type;//图片名称 + $pic_url =$path . $pic_name;//上传后图片路径+名称 + + if (move_uploaded_file($name_tmp, $pic_url)) { //临时文件转移到目标文件夹 + /* $_POST['name'] = $pic_name; + $_POST['topic'] = $imageType; + $_POST['albumPath'] = $pic_url;*/ + echo json_encode(array("status"=>"0","file_name"=>$pic_name,"file_path"=>$request->domain().'/'.$pic_url)); + die(); + } else { + echo json_encode(array("error"=>"上传有误,请检查服务器配置!")); + die(); + } + //} + } +} diff --git a/application/admincpp/controller/Index.php b/application/admincpp/controller/Index.php new file mode 100644 index 0000000..4770459 --- /dev/null +++ b/application/admincpp/controller/Index.php @@ -0,0 +1,196 @@ +send(); + exit; + } + } + /** + * 获取菜单列表 + * @author hjc + * @date 2024-05-14 + */ + public function getMenuList(Request $request){ + $data=$request->param(); + // $this->checkToken($data['Token']); + //升序获取菜单列表 + $result = Db::name('menu') + ->field('id,pid,menu_name,url,seq_on,menu_icon') + ->order('seq_on') + ->select(); + //找二级菜单 + $second=[]; + foreach ($result as $key => $value) { + if($value['pid'] != ''){ + //二级 + $second[]=$value; + unset($result[$key]); + } + } + //二级组装到一级 + $result=array_values($result); + foreach ($result as $key => $value) { + $result[$key]['type']=1; + $result[$key]['No']=$key+1; + foreach ($second as $k => $v) { + if($value['menu_id']==$v['pid']){ + $v['type']=2; + $result[$key]['children'][]=$v; + } + } + } + if($result){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json_encode(['status'=>$status,'msg'=>$msg,'data'=>$result]); + } + /** + * 权限菜单列表 + * @author hjc + * @date 2024-05-14 + */ + public function getPermissionMenu(Request $request){ + $data=$request->param(); + //$this->checkToken($data['Token']); + //升序获取菜单列表 + $result=Db::name('menu')->field('id,pid,menu_name,url,seq_on,menu_icon')->order('seq_on')->select(); + //找二级三级菜单 + $second=[]; + foreach ($result as $key => $value) { + if($value['pid'] != ''){ + $second[]=$value; + unset($result[$key]); + } + } + //二级组装到一级 + foreach ($result as $key => $value) { + $result[$key]['type']=1; + foreach ($second as $k => $v) { + if($value['menu_id']==$v['pid']){ + $v['type']=2; + $result[$key]['second'][]=$v; + } + } + } + if($result){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json_encode(['status'=>$status,'msg'=>$msg,'data'=>$result]); + } + + public function getMenuOption(Request $request){ + $data=$request->param(); + // $this->checkToken($data['Token']); + //升序获取菜单列表 + $result = Db::name('menu') + ->field('id,pid,menu_name') + //->where('status',0) + ->order('seq_on') + ->select(); + //找二级三级菜单 + $second=[]; + // $third=[]; + foreach ($result as $key => $value) { + if($value['pid'] != ''){ + //二级 + $second[$key]['value']=$value['id']; + $second[$key]['label']=$value['menu_name']; + $second[$key]['pid']=$value['pid']; + unset($result[$key]); + } + } + //二级组装到一级 + $first=[]; + foreach ($result as $key => $value) { + $first[$key]['value']=$value['id']; + $first[$key]['label']=$value['menu_name']; + foreach ($second as $k => $v) { + if($value['menu_id']==$v['pid']){ + unset($v['pid']); + $first[$key]['children'][]=$v; + } + } + } + if($first){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json_encode(['status'=>$status,'msg'=>$msg,'data'=>$first]); + } + //获取当前菜单最大序号 + public function getMaxSeq($map){ + $result=Db::name('menu')->field('seq_on')->where($map)->order('seq_on','desc')->find(); + if($result){ + return $result['seq_on']+1; + }else{ + return 1; + } + } + //添加菜单 + public function doaddMenu(Request $request){ + //获取一下表单提交的数据,并保存在变量中 + $data=$request->param(); + // $this->checkToken($data['Token']); + unset($data['Token']); + $menu_id=md5(uniqid(''));//一级菜单id + $create_time=date('Y-m-d H:i:s');//插入时间 + $data1=[]; + //添加一级菜单 + if(empty($data['father'])){ + $map['pid']=''; + }else{ + $map['pid']=$data['father']; + } + //获取一级菜单最大序号 + $seq_on=$this->getMaxSeq($map); + $data1=$map; + if(!empty($data['urs'])){ + $data1['url']=$data['url']; + } + if(!empty($data['menu_icon'])){ + $data1['menu_icon']=$data['menu_icon']; + } + $data1['id']=$menu_id; + $data1['menu_name']=$data['title']; + $data1['seq_on']=$seq_on; + $data1['create_time']=$create_time; + $res=Db::name('menu')->insert($data1); + if($res){ + $status=1; + $msg='添加菜单成功!!'; + }else{ + $status=0; + $msg='添加菜单失败!!'; + } + return json_encode(['status'=>$status,'msg'=>$msg]); + } +} diff --git a/application/admincpp/controller/Login.php b/application/admincpp/controller/Login.php new file mode 100644 index 0000000..a06e815 --- /dev/null +++ b/application/admincpp/controller/Login.php @@ -0,0 +1,92 @@ +view->fetch('login'); + }else{ + //登录信息超时 + $log_time=session('admincpp_user_login_time')-time(); + if($log_time < 0){ + return $this->view->fetch('login'); + } + } + return $this->view->fetch('nav'); + } + /** + * 验证用户身份. + * + * @return \think\Response + */ + public function login(Request $request) + { + //判断是否是post方法发送的数据:如果是则开始登陆 + if(Request::instance()->isPost()){ + $user_name=Request::instance()->param("username"); + $password=Request::instance()->param('password'); + if(empty($user_name) || empty($password)){ + $result=['status'=>0,'msg'=>'用户名或密码为空']; + return json($result); + } + //数据查询 + $infor=Db::name("user") + ->field('id,user_name,user_head') + ->where('phone',$user_name) + ->where('password',md5($password)) + ->where("status",1) + ->where('type',1) + ->find(); + if(empty($infor)){ + $result=['status'=>0,'msg'=>'用户不存在或密码不正确']; + }else{ + //Session::set('admin_user_type',$infor['role_id']); + Session::set('admincpp_user_id',$infor['id']); + //Session::set('admin_mechanism_id',$infor['mechanism_id']); + $time_out=time()+3600; + Session::set('admincpp_user_login_time',$time_out); + $result=['status'=>1,'msg'=>'登录成功','token'=>1]; + } + return json($result); + } + //如果不是post,则返回登陆界面 + else{ + $result=['status'=>0,'msg'=>'未获取到参数']; + return json($result); + } + } + /** + * 退出登录 + * + * @param \think\Request $request + * @return \think\Response + */ + public function logout(Request $request) + { + Session::clear(); + return json(['status'=>1,'msg'=>'退出成功']); + } + /** + * 超时登录推出跳转页面 + * + * @param \think\Request $request + * @return \think\Response + */ + public function logoutJump(Request $request) + { + return $this->view->fetch('logoutJump'); + } +} diff --git a/application/admincpp/controller/Menu.php b/application/admincpp/controller/Menu.php new file mode 100644 index 0000000..2d9dc63 --- /dev/null +++ b/application/admincpp/controller/Menu.php @@ -0,0 +1,326 @@ +send(); + exit; + }*/ + } + public function index() + { + // $this->alreadyLogin(); + return $this->view->fetch('index'); + } + public function nav() + { + // $this->alreadyLogin(); + return $this->view->fetch('nav'); + } + public function getUserInfor(){ + $data=Db::name("user") + ->field('user_name,user_head') + // admincpp 模块登录写入的是 admincpp_user_id(否则这里会变成 id IS NULL) + ->where('id',session('admincpp_user_id')) + ->find(); + $result=['status'=>1,'msg'=>'查询成功','infro'=>$data]; + return json($result); + } + /** + * 获取菜单列表 + * @author hjc + * @date 2024-05-14 + */ + public function getMenuList(Request $request){ + $data=$request->param(); + // $this->checkToken($data['Token']); + //升序获取菜单列表 + $result = Db::name('menu') + ->field('id,pid,menu_name,url,seq_on,menu_icon') + ->order('seq_on') + ->select(); + //找二级菜单 + $second=[]; + foreach ($result as $key => $value) { + if($value['pid'] != '' && $value['pid'] != 0){ + //二级 + $value['grade']="二级菜单"; + $second[]=$value; + unset($result[$key]); + }else{ + $result[$key]['grade']="一级菜单"; + } + } + //二级组装到一级 + $result=array_values($result); + foreach ($result as $key => $value) { + $result[$key]['type']=1; + $result[$key]['No']=$key+1; + foreach ($second as $k => $v) { + if($value['id']==$v['pid']){ + $v['type']=2; + $result[$key]['children'][]=$v; + } + } + } + if($result){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json(['status'=>$status,'msg'=>$msg,'data'=>$result]); + } + /** + * 权限菜单列表 + * @author hjc + * @date 2024-05-14 + */ + public function getPermissionMenu(Request $request){ + $data=$request->param(); + //$this->checkToken($data['Token']); + //升序获取菜单列表 + $result=Db::name('menu')->field('id,pid,menu_name,url,seq_on,menu_icon')->order('seq_on')->select(); + //找二级三级菜单 + $second=[]; + foreach ($result as $key => $value) { + if($value['pid'] != ''){ + $second[]=$value; + unset($result[$key]); + } + } + //二级组装到一级 + foreach ($result as $key => $value) { + $result[$key]['type']=1; + foreach ($second as $k => $v) { + if($value['menu_id']==$v['pid']){ + $v['type']=2; + $result[$key]['second'][]=$v; + } + } + } + if($result){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json(['status'=>$status,'msg'=>$msg,'data'=>$result]); + } + + public function getMenuOption(Request $request){ + $data=$request->param(); + // $this->checkToken($data['Token']); + //升序获取菜单列表 + $result = Db::name('menu') + ->field('id,pid,menu_name') + //->where('status',0) + ->order('seq_on') + ->select(); + //找二级三级菜单 + $second=[]; + // $third=[]; + foreach ($result as $key => $value) { + if($value['pid'] != ''){ + //二级 + $second[$key]['value']=$value['id']; + $second[$key]['label']=$value['menu_name']; + $second[$key]['pid']=$value['pid']; + unset($result[$key]); + } + } + //二级组装到一级 + $first=[]; + foreach ($result as $key => $value) { + $first[$key]['value']=$value['id']; + $first[$key]['label']=$value['menu_name']; + foreach ($second as $k => $v) { + if($value['menu_id']==$v['pid']){ + unset($v['pid']); + $first[$key]['children'][]=$v; + } + } + } + if($first){ + $status=1; + $msg="获取数据成功!!"; + } + else{ + $status=0; + $msg="获取数据失败!!"; + } + return json(['status'=>$status,'msg'=>$msg,'data'=>$first]); + } + //获取当前菜单最大序号 + public function getMaxSeq($map){ + $result=Db::name('menu')->field('seq_on')->where($map)->order('seq_on','desc')->find(); + if($result){ + return $result['seq_on']+1; + }else{ + return 1; + } + } + //添加菜单 + public function doaddMenu(Request $request){ + //获取一下表单提交的数据,并保存在变量中 + $data=$request->param(); + // $this->checkToken($data['Token']); + unset($data['Token']); + $menu_id=md5(uniqid(''));//一级菜单id + $create_time=date('Y-m-d H:i:s');//插入时间 + $data1=[]; + //添加一级菜单 + if(empty($data['father'])){ + $map['pid']=''; + }else{ + $map['pid']=$data['father']; + } + //获取一级菜单最大序号 + $seq_on=$this->getMaxSeq($map); + $data1=$map; + if(!empty($data['urs'])){ + $data1['url']=$data['url']; + } + if(!empty($data['menu_icon'])){ + $data1['menu_icon']=$data['menu_icon']; + } + $data1['id']=$menu_id; + $data1['menu_name']=$data['title']; + $data1['seq_on']=$seq_on; + $data1['create_time']=$create_time; + $res=Db::name('menu')->insert($data1); + if($res){ + $status=1; + $msg='添加菜单成功!!'; + }else{ + $status=0; + $msg='添加菜单失败!!'; + } + return json(['status'=>$status,'msg'=>$msg]); + } + /** + * 修改菜单详情 + * @param $data 菜单id(一级菜单、二级菜单) + * @param $data 菜单详情 + * @author hjc + * @date 2024-05-14 + */ + public function updMenu(Request $request){ + $data=$request->param(); + $result=Db::name('menu')->field('id,pid,menu_name,url,menu_icon')->where('menu_id',$data['menu_id'])->find(); + $result['type']=1; + if(!empty($result['pid'])){ + $result['type']=2; + $result['father']=$result['pid']; + } + unset($result['pid']); + if($result){ + $status=1; + $msg="获取数据成功!!"; + }else{ + $status=0; + $msg="获取数据失败!!"; + } + return json_encode(['status'=>$status,'msg'=>$msg,'data'=>$result]); + } + public function doupdMenu(Request $request){ + $data=$request->param(); + $data1=[]; + //添加一级菜单 + if(empty($data['father'])){ + $data1['pid']=''; + if(!empty($data['menu_icon'])){ + $data1['menu_icon']=$data['menu_icon']; + } + }else{ + $data1['pid']=$data['father']; + //获取一级菜单最大序号 + if(!empty($data['url'])){ + $data1['url']=$data['url']; + } + } + $data1['menu_name']=$data['menu_name']; + $res=Db::name('menu')->where('id',$data['id'])->update($data1); + if($res){ + $status=1; + $msg='修改菜单成功!!'; + }else{ + $status=0; + $msg='修改菜单失败!!'; + } + return json_encode(['status'=>$status,'msg'=>$msg]); + } + /** + * 设置菜单状态 + * @param $data 一级菜单id、二级菜单id + * @author hjc + * @date 2024-05-14 + */ + public function setMenuStatus(Request $request){ + $data=$request->param(); + $this->checkToken($data['Token']); + $res=Db::name('menu')->where('id',$data['id'])->update(['status'=>$data['status']]); + if($res){ + $status=1; + $msg='设置成功!!'; + }else{ + $status=0; + $msg='设置失败!!'; + } + return json_encode(['status'=>$status,'msg'=>$msg]); + } + /** + * 删除菜单 + * @param $data 一级菜单id、二级菜单id + *@author hjc + * @date 2024-05-14 + */ + public function delMenu(Request $request){ + $data=$request->param(); + $res=Db::name('menu')->where('id',$data['id'])->find(); + //一级菜单 + if($res['pid']==''){ + $map1['id|pid']=$data['id']; + $map2['id']=['in',[$data['id'],$res['pid']]]; + }else{ + $map1['id']=$data['id']; + $map2['id']=$data['id']; + } + // 启动事务 + Db::startTrans(); + try{ + //删除二级菜单 + Db::name('menu')->where($map1)->delete(); + // Db::table('t_nv_role_menu')->where($map2)->delete(); + // 提交事务 + Db::commit(); + $status=1; + $msg='删除菜单成功!!'; + } catch (\Exception $e) { + Log::record($e->getMessage(),'error'); + // 回滚事务 + Db::rollback(); + $status=0; + $msg='删除菜单失败!!'; + } + return json_encode(['status'=>$status,'msg'=>$msg]); + } +} diff --git a/application/admincpp/controller/Wechatinfro.php b/application/admincpp/controller/Wechatinfro.php new file mode 100644 index 0000000..2bf1ac1 --- /dev/null +++ b/application/admincpp/controller/Wechatinfro.php @@ -0,0 +1,106 @@ +view->fetch('userList'); + } + + public function getWechatUserList(Request $request){ + $post=$request->param(); + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1; + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 30; + $where=[]; + if(isset($post['title']) && !empty($post['title'])){ + $where['nickname']=['like','%'.$post['title'].'%']; + } + $res = Db::name('wechat_user') + ->field('uid,nickname,headimg,create_time') + ->where($where) + ->order('create_time desc') + ->paginate($page_size,false,['page'=>$page]); + $lists=$res->items(); + $start=($page-1)*$page_size; + foreach($lists as $k=>$v){ + $start+=1; + $lists[$k]['no_id']=$start; + } + $result['lists'] =$lists; + $result['lastPage'] = $res->lastPage(); + $result['currentPage'] = $res->currentPage(); + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } + + public function wechatRecordList() + { + return $this->view->fetch('wechatRecordList'); + } + + public function getWechatRecordList(Request $request){ + $post=$request->param(); + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1; + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 30; + $where=[]; + if(isset($post['title']) && !empty($post['title'])){ + $where['name']=['like','%'.$post['title'].'%']; + } + $res = Db::name('cpp_calculate_record') + ->field('id,name,bone_age,uterus_length,uterus_width,lh_peak,fsh_peak,fsh_3m,igf1,calculate_result,risk_level,create_time') + ->where($where) + ->order('create_time desc') + ->paginate($page_size,false,['page'=>$page]); + $lists=$res->items(); + $start=($page-1)*$page_size; + foreach($lists as $k=>$v){ + $start+=1; + $lists[$k]['no_id']=$start; + } + $result = array(); + $result['lists'] =$lists; + $result['lastPage'] = $res->lastPage(); + $result['currentPage'] = $res->currentPage(); + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } + + public function feedbackList() + { + return $this->view->fetch('feedbackList'); + } + + public function getFeedbackList(Request $request){ + $post=$request->param(); + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1; + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 30; + $res = Db::name('cpp_feedback') + ->field('id,uid,content,create_time') + ->order('create_time desc') + ->paginate($page_size,false,['page'=>$page]); + $lists=$res->items(); + $start=($page-1)*$page_size; + foreach($lists as $k=>$v){ + $start+=1; + $lists[$k]['no_id']=$start; + } + $result = array(); + $result['lists'] =$lists; + $result['lastPage'] = $res->lastPage(); + $result['currentPage'] = $res->currentPage(); + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } +} diff --git a/application/admincpp/controller/Wechatset.php b/application/admincpp/controller/Wechatset.php new file mode 100644 index 0000000..3875ead --- /dev/null +++ b/application/admincpp/controller/Wechatset.php @@ -0,0 +1,182 @@ +send(); + exit; + }*/ + } + /** + * 小程序资讯信息页面 + */ + public function wechatRealTimeInfo() + { + return $this->view->fetch('realTimeInfo'); + } + /** + * 小程序添加资讯信息页面 + */ + public function wechatRealTimeInfoAdd() + { + return $this->view->fetch('realTimeInfoAdd'); + } + /** + * 小程序添加资讯信息页面 + */ + public function wechatRealTimeInfoUpdate(Request $request) + { + $id=$request->get('id'); + $this->assign('id',$id); + return $this->view->fetch('realTimeInfoUpdate'); + } + /** + * 查询小程序资讯信息 + * @author hjc + * @date 2024-05-14 + */ + public function getWechatRealTimeInfoList(Request $request){ + $post=$request->param(); + + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1;//页数 + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 30;//每页条数 + //搜索 + $where=[]; + if(isset($post['title']) && !empty($post['title'])){ + $where['title_plain']=['like','%'.$post['title'].'%']; + } + //升序获取菜单列表 + $res = Db::name('wechat_real_time_info') + ->field('id,title_plain,thumbnail,excerpt_plain,url,create_time') + ->where($where) + ->order('create_time desc') + ->paginate($page_size,false,['page'=>$page]); + $lists=$res->items(); + $start=($page-1)*$page_size; + foreach($lists as $k=>$v){ + $start+=1; + $lists[$k]['no_id']=$start; + } + $result['lists'] =$lists; + $result['lastPage'] = $res->lastPage();//总页数 + $result['currentPage'] = $res->currentPage();//当前页 +// $result['total'] = $res->total();//数据总条数 + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } + public function wechatRecordList() + { + return $this->view->fetch('wechatRecordList'); + } + /** + * 删除小程序资讯信息 + * @author hjc + * @date 2024-05-14 + */ + public function delWechatRealTimeInfo(Request $request){ + $post=$request->param(); + if(!isset($post['id']) || empty($post['id']) ){ + $result['erro'] = -1; + $result['msg'] = "请选择要删除的记录"; + return json($result); + } + $id=Db::name('wechat_real_time_info')->where('id',$post['id'])->value('id'); + if(empty($id)){ + $result['erro'] = -1; + $result['msg'] = "该记录异常,无法删除"; + return json($result); + } + $res=Db::name('wechat_real_time_info')->where('id',$id)->delete(); + if($res > 0){ + $result['erro'] = 0; + $result['msg'] = '删除成功'; + }else{ + $result['erro'] = -1; + $result['msg'] = '删除失败'; + } + return json($result); + } + /*** + *保存小程序资讯信息 + **/ + public function saveWechatRealTimeInfo(Request $request){ + $post=$request->param(); + + if(!isset($post['title_plain']) || empty($post['title_plain']) ){ + $result['erro'] = -1; + $result['msg'] = "未传入标题"; + return json($result); + } + if(!isset($post['thumbnail']) || empty($post['thumbnail']) ){ + $result['erro'] = -1; + $result['msg'] = "未传入图片"; + return json($result); + } + if(!isset($post['excerpt_plain']) || empty($post['excerpt_plain']) ){ + $result['erro'] = -1; + $result['msg'] = "未传入简介"; + return json($result); + } + if(!isset($post['url']) || empty($post['url'])){ + $result['erro'] = -1; + $result['msg'] = "未传入资讯跳转链接"; + return json($result); + } + //提交数据 + $data=[]; + $data['title_plain']=$post['title_plain'];//标题 + $data['thumbnail']=$post['thumbnail'];//图片 + $data['excerpt_plain']=$post['excerpt_plain'];//介绍 + $data['url']=$post['url'];//跳转连接 + if(isset($post['id']) && !empty($post['id'])){ + //修改数据 + $res=Db::name('wechat_real_time_info')->where('id',$post['id'])->update($data); + }else{ + //保存记录数据 + $data['id']=md5(time().rand(100000,999999)); + $data['create_time']=date('Y-m-d H:i:s'); + $res=Db::name('wechat_real_time_info')->insert($data); + } + if($res===false){ + $result['erro'] = -1; + $result['msg'] = "添加失败"; + return json($result); + }else{ + $result['erro'] = 0; + $result['msg'] = "添加成功"; + return json($result); + } + } + /** + *查询资讯信息详情 + */ + public function getRealTimeInfoDetail(Request $request){ + $post=$request->param(); + if(!isset($post['id']) || empty($post['id'])){ + $result['erro'] = -1; + $result['msg'] = "未传入资讯id"; + return json($result); + } + //修改数据 + $result['infro']=Db::name('wechat_real_time_info')->where('id',$post['id'])->find(); + $result['erro'] = 0; + $result['msg'] = "查询成功"; + return json($result); + } +} diff --git a/application/admincpp/model/Permission.php b/application/admincpp/model/Permission.php new file mode 100644 index 0000000..f4b96bf --- /dev/null +++ b/application/admincpp/model/Permission.php @@ -0,0 +1,11 @@ + + + + + + + + 系统概览 + + + + +
+
欢迎使用后台管理系统
+
这里是系统概览,实时展示系统统计数据。
+
+
数据加载中...
+
+
+ + + + + + diff --git a/application/admincpp/view/login/login.html b/application/admincpp/view/login/login.html new file mode 100644 index 0000000..e405bd3 --- /dev/null +++ b/application/admincpp/view/login/login.html @@ -0,0 +1,91 @@ + + + + + + + + + 登录 - CPP生长减速预测模型后台 + + + + + +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/application/admincpp/view/login/logoutJump.html b/application/admincpp/view/login/logoutJump.html new file mode 100644 index 0000000..b98b51f --- /dev/null +++ b/application/admincpp/view/login/logoutJump.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/admincpp/view/login/nav.html b/application/admincpp/view/login/nav.html new file mode 100644 index 0000000..f83141f --- /dev/null +++ b/application/admincpp/view/login/nav.html @@ -0,0 +1,41 @@ + + + + + + + + CPP生长减速预测模型后台 + + + + + + +
+
+ +
+
+ +
+
CPP生长减速预测模型后台
+
+ + 退出 +
+
+
+ +
+ + + + + + \ No newline at end of file diff --git a/application/admincpp/view/wechatinfro/feedbackList.html b/application/admincpp/view/wechatinfro/feedbackList.html new file mode 100644 index 0000000..bad49d0 --- /dev/null +++ b/application/admincpp/view/wechatinfro/feedbackList.html @@ -0,0 +1,25 @@ + + + + + + + + + + + CPP反馈列表 + + +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/application/admincpp/view/wechatinfro/userList.html b/application/admincpp/view/wechatinfro/userList.html new file mode 100644 index 0000000..c115cff --- /dev/null +++ b/application/admincpp/view/wechatinfro/userList.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + 用户管理 + + + +
+ +
+
+
+
+
+
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+ + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/application/admincpp/view/wechatinfro/wechatRecordList.html b/application/admincpp/view/wechatinfro/wechatRecordList.html new file mode 100644 index 0000000..bcdcc31 --- /dev/null +++ b/application/admincpp/view/wechatinfro/wechatRecordList.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + CPP计算记录 + + +
+ +
+
+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/application/admincpp/view/wechatset/realTimeInfo.html b/application/admincpp/view/wechatset/realTimeInfo.html new file mode 100644 index 0000000..50ff7be --- /dev/null +++ b/application/admincpp/view/wechatset/realTimeInfo.html @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + 用户管理 + + + +
+ +
+
+
+
+
+
+ +
+ + +
+
+ +
+ + 新增 +
+
+ +
+ +
+
+
+ + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/application/admincpp/view/wechatset/realTimeInfoAdd.html b/application/admincpp/view/wechatset/realTimeInfoAdd.html new file mode 100644 index 0000000..98ce89d --- /dev/null +++ b/application/admincpp/view/wechatset/realTimeInfoAdd.html @@ -0,0 +1,319 @@ + + + + + + + + +新建编辑 + + + + + + + + +
+ +
+
+ +
+ 保存 +
+
+ +
+
+
+ +
+

+ * + 请输入标题: +

+
+ + + 0/ + 30 + +
+

+ * + 请输入介绍: +

+
+ +
+

+ * + 请输入资讯跳转链接: +

+
+ +
+

+ *请上传资讯图片: + +

+
+
+ + + +
+ +
+ +
+ + +
+
+ + +
+ + + + + + + + + + + diff --git a/application/admincpp/view/wechatset/realTimeInfoUpdate.html b/application/admincpp/view/wechatset/realTimeInfoUpdate.html new file mode 100644 index 0000000..9875789 --- /dev/null +++ b/application/admincpp/view/wechatset/realTimeInfoUpdate.html @@ -0,0 +1,320 @@ + + + + + + + + +新建编辑 + + + + + + + + +
+ +
+
+
+ 保存 +
+
+ +
+
+
+ + +
+

+ * + 请输入标题: +

+
+ + + 0/ + 30 + +
+

+ * + 请输入介绍: +

+
+ +
+

+ * + 请输入资讯跳转链接: +

+
+ +
+

+ *请上传资讯图片: + +

+
+
+ + + +
+ +
+ +
+ + +
+
+ + +
+ + + + + + + + + + + diff --git a/application/app/controller/Cppwechat.php b/application/app/controller/Cppwechat.php new file mode 100644 index 0000000..1c88cb7 --- /dev/null +++ b/application/app/controller/Cppwechat.php @@ -0,0 +1,399 @@ +Appid = 'wx0847525a15342a46'; + $this->Appsecret = '71cf8c524580ba6b262e6dc52d44da4c'; + $this->TokenKey = 'ruilai340u20k4'; + } + + /********************************* 首页 *********************************/ + public function selectRealtimeInfoList(Request $request) + { + $post = $request->param(); + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1; + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 20; + + $res = Db::name('wechat_real_time_info') + ->field('title_plain,thumbnail,excerpt_plain,url') + ->order('create_time desc') + ->paginate($page_size, false, ['page' => $page]); + + $result = array(); + $result['lists'] = $res->items(); + $result['lastPage'] = $res->lastPage(); + $result['currentPage'] = $res->currentPage(); + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } + + /********************************* 计算 *********************************/ + public function calculate(Request $request) + { + $post = $request->param(); + + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "未传入用户id"; + return json_encode($result); + } + if (!isset($post['name']) || empty($post['name'])) { + $result['erro'] = -1; + $result['msg'] = "未传入姓名"; + return json_encode($result); + } + if (!isset($post['bone_age']) || empty($post['bone_age']) || !is_numeric($post['bone_age'])) { + $result['erro'] = -1; + $result['msg'] = "未传入初诊骨龄或数据格式不对"; + return json_encode($result); + } + if (!isset($post['uterus_length']) || empty($post['uterus_length']) || !is_numeric($post['uterus_length'])) { + $result['erro'] = -1; + $result['msg'] = "未传入初诊子宫长或数据格式不对"; + return json_encode($result); + } + if (!isset($post['uterus_width']) || empty($post['uterus_width']) || !is_numeric($post['uterus_width'])) { + $result['erro'] = -1; + $result['msg'] = "未传入初诊子宫宽或数据格式不对"; + return json_encode($result); + } + if (!isset($post['lh_peak']) || empty($post['lh_peak']) || !is_numeric($post['lh_peak'])) { + $result['erro'] = -1; + $result['msg'] = "未传入LH峰值或数据格式不对"; + return json_encode($result); + } + if (!isset($post['fsh_peak']) || empty($post['fsh_peak']) || !is_numeric($post['fsh_peak'])) { + $result['erro'] = -1; + $result['msg'] = "未传入FSH峰值或数据格式不对"; + return json_encode($result); + } + if (!isset($post['fsh_3m']) || empty($post['fsh_3m']) || !is_numeric($post['fsh_3m'])) { + $result['erro'] = -1; + $result['msg'] = "未传入治疗3月FSH或数据格式不对"; + return json_encode($result); + } + if (!isset($post['igf1']) || empty($post['igf1']) || !is_numeric($post['igf1'])) { + $result['erro'] = -1; + $result['msg'] = "未传入基础IGF-1或数据格式不对"; + return json_encode($result); + } + + // 参数取值 + $bone_age = floatval($post['bone_age']); + $uterus_length = floatval($post['uterus_length']); + $uterus_width = floatval($post['uterus_width']); + $lh_peak = floatval($post['lh_peak']); + $fsh_peak = floatval($post['fsh_peak']); + $fsh_3m = floatval($post['fsh_3m']); + $igf1 = floatval($post['igf1']); + + // 交互项: 骨龄 x 子宫长 + $interaction_ba_ul = $bone_age * $uterus_length; + // 比值项: LH峰值 / FSH峰值 + $lh_fsh_ratio = $lh_peak / $fsh_peak; + // log(FSH峰值) + $log_fsh_peak = log($fsh_peak); + + // 预测模型 + $logit = -2.8457 + + 0.1198 * $interaction_ba_ul + + 1.0411 * $log_fsh_peak + - 0.4832 * $fsh_3m + - 0.0044 * $igf1 + + 0.4207 * $lh_fsh_ratio + - 0.8303 * $uterus_width; + + // sigmoid + $exp_val = exp($logit); + $probability = $exp_val / (1 + $exp_val); + + // 风险分级 + if ($probability < 0.430) { + $risk_level = '低风险'; + $suggestion = '继续目前GnRHa治疗方案,按常规6个月随访即可,关注身高增长趋势。'; + } elseif ($probability <= 0.572) { + $risk_level = '中风险'; + $suggestion = '建议缩短随访间隔至3个月,关注GV下降趋势,必要时联合rhGH。'; + } else { + $risk_level = '高风险'; + $suggestion = '建议每2-3个月密切随访,积极监测生长速率变化,尽早考虑rhGH联合干预。'; + } + + // 保存记录 + $data = []; + $data['id'] = md5(time() . rand(100000, 999999)); + $data['uid'] = $post['uid']; + $data['name'] = $post['name']; + $data['bone_age'] = $bone_age; + $data['uterus_length'] = $uterus_length; + $data['uterus_width'] = $uterus_width; + $data['lh_peak'] = $lh_peak; + $data['fsh_peak'] = $fsh_peak; + $data['fsh_3m'] = $fsh_3m; + $data['igf1'] = $igf1; + $data['calculate_result'] = round($probability, 4); + $data['risk_level'] = $risk_level; + $data['create_time'] = date('Y-m-d H:i:s'); + + $res = Db::name('cpp_calculate_record')->insert($data); + if ($res === false) { + $result['erro'] = -1; + $result['msg'] = '计算失败,请重试'; + return json_encode($result); + } else { + $result['erro'] = 0; + $result['msg'] = '计算成功'; + $result['calculate_result'] = round($probability, 4); + $result['risk_level'] = $risk_level; + $result['suggestion'] = $suggestion; + return json_encode($result); + } + } + + /********************************* 记录 *********************************/ + public function selectRecordList(Request $request) + { + $post = $request->param(); + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "未传入用户id"; + return json_encode($result); + } + $page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1; + $page_size = isset($post['page_size']) && !empty($post['page_size']) ? $post['page_size'] : 30; + + $res = Db::name('cpp_calculate_record') + ->field('id,name,bone_age,uterus_length,uterus_width,lh_peak,fsh_peak,fsh_3m,igf1,calculate_result,risk_level,create_time') + ->where('uid', $post['uid']) + ->order('create_time desc') + ->paginate($page_size, false, ['page' => $page]); + + $result = array(); + $result['lists'] = $res->items(); + $result['lastPage'] = $res->lastPage(); + $result['currentPage'] = $res->currentPage(); + $result['erro'] = 0; + $result['msg'] = '查询成功'; + return json($result); + } + + public function deleteRecord(Request $request) + { + $post = $request->param(); + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "未传入用户id"; + return json($result); + } + if (!isset($post['id']) || empty($post['id'])) { + $result['erro'] = -1; + $result['msg'] = "请选择要删除的记录"; + return json($result); + } + $id = Db::name('cpp_calculate_record') + ->where('id', $post['id']) + ->where('uid', $post['uid']) + ->value('id'); + if (empty($id)) { + $result['erro'] = -1; + $result['msg'] = '该记录异常,无法删除'; + return json($result); + } + $res = Db::name('cpp_calculate_record')->where('id', $id)->delete(); + if ($res > 0) { + $result['erro'] = 0; + $result['msg'] = '删除成功'; + } else { + $result['erro'] = -1; + $result['msg'] = '删除失败'; + } + return json($result); + } + + /********************************* 反馈 *********************************/ + public function submitFeedback(Request $request) + { + $post = $request->param(); + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "请先登录"; + return json_encode($result); + } + if (!isset($post['content']) || empty(trim($post['content']))) { + $result['erro'] = -1; + $result['msg'] = "请输入反馈内容"; + return json_encode($result); + } + $data = []; + $data['uid'] = $post['uid']; + $data['content'] = trim($post['content']); + $data['create_time'] = date('Y-m-d H:i:s'); + $res = Db::name('cpp_feedback')->insert($data); + if ($res) { + $result['erro'] = 0; + $result['msg'] = '感谢您的反馈'; + } else { + $result['erro'] = -1; + $result['msg'] = '提交失败,请重试'; + } + return json_encode($result); + } + + /********************************* 登录 *********************************/ + public function wechatLogin(Request $request) + { + $post = $request->param(); + if (!isset($post['code']) || empty($post['code'])) { + $result['erro'] = -1; + $result['msg'] = "未传入code"; + return json_encode($result); + } + if (!isset($post['encryptedData']) || empty($post['encryptedData'])) { + $result['erro'] = -1; + $result['msg'] = "未传入encryptedData"; + return json_encode($result); + } + if (!isset($post['iv']) || empty($post['iv'])) { + $result['erro'] = -1; + $result['msg'] = "未传入iv"; + return json_encode($result); + } + + $encryptedData = $post['encryptedData']; + $iv = $post['iv']; + $code = $post['code']; + $URL = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $this->Appid . "&secret=" . $this->Appsecret . "&js_code=$code&grant_type=authorization_code"; + $apiData = json_decode($this->httpGet($URL), true); + + if (!isset($apiData['errcode'])) { + $sessionKey = $apiData['session_key']; + $errCode = $this->decryptData($sessionKey, $this->Appid, $encryptedData, $iv, $data); + if ($errCode == 0) { + $record = json_decode($data, true); + $res = $this->saveUseInfor($record); + return json_encode($res); + } else { + $result['erro'] = -1; + $result['msg'] = '解析接口返回数据失败,请重试!'; + return json_encode($result); + } + } else { + $result['erro'] = -1; + $result['msg'] = $apiData['errmsg']; + return json_encode($result); + } + } + + private function saveUseInfor($record) + { + $data = array(); + $data['nickname'] = $record['nickName']; + $data['headimg'] = $record['avatarUrl']; + + $rec = Db::name('wechat_user')->field('uid')->where('openid', $record['openId'])->find(); + + if (empty($rec)) { + $data['openid'] = $record['openId']; + $data['create_time'] = date('Y-m-d H:i:s'); + $res = Db::name('wechat_user')->insertGetId($data); + $uid = $res; + } else { + $res = Db::name('wechat_user')->where('uid', $rec['uid'])->update($data); + $uid = $rec['uid']; + } + if ($res === false) { + $result['erro'] = -1; + $result['msg'] = '保存信息失败'; + return $result; + } else { + $result['erro'] = 0; + $result['msg'] = '登录成功'; + $result['uid'] = $uid; + return $result; + } + } + + public function getToken(Request $request) + { + $post = $request->param(); + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "未传登录用户id"; + return json_encode($result); + } + if (!isset($post['phone']) || empty($post['phone'])) { + $result['erro'] = -1; + $result['msg'] = "未传登录用户账号"; + return json_encode($result); + } + $arr = array(); + $arr['phone'] = $post['phone']; + $arr['status'] = 1; + $res = Db::name('accounthosting')->where($arr)->value('id'); + if ($res != $post['uid']) { + $result['erro'] = -1; + $result['msg'] = '账号异常'; + return json_encode($result); + } + $result['erro'] = 0; + $result['msg'] = '登录成功'; + $result['token'] = $this->authcode($post['uid'] . '_' . time(), 'ENCODE', $this->TokenKey, 60 * 60 * 3); + return json_encode($result); + } + + private function checkToken($token, $uid) + { + $res = $this->authcode($token, 'DECODE', $this->TokenKey, 60 * 60 * 3); + if (empty($res)) { + return false; + } else { + $data = explode('_', $res); + if ($data[0] == $uid) { + return true; + } else { + return false; + } + } + } + + public function getUserBaseInfo(Request $request) + { + $post = $request->param(); + if (!isset($post['uid']) || empty($post['uid'])) { + $result['erro'] = -1; + $result['msg'] = "未传入登录人id"; + return json_encode($result); + } + if (!isset($post['token']) || empty($post['token'])) { + $result['erro'] = -1; + $result['msg'] = "未传入token"; + return json_encode($result); + } + if ($this->checkToken($post['token'], $post['uid']) === false) { + $result['erro'] = -1; + $result['msg'] = 'token异常'; + return json_encode($result); + } + $result['infor'] = Db::name('accounthosting') + ->field('name,headimg,balance') + ->where('id', $post['uid']) + ->find(); + $result['erro'] = 0; + $result['msg'] = '获取数据成功'; + return json_encode($result); + } +} diff --git a/application/common/config/BusinessConfig.php b/application/common/config/BusinessConfig.php index d7c842a..398f73a 100644 --- a/application/common/config/BusinessConfig.php +++ b/application/common/config/BusinessConfig.php @@ -1,21 +1,12 @@ 'wechat_real_time_info', 'calculate_record' => 'wechat_calculate_record', ], + self::BUSINESS_ADMINCPP => [ + 'user' => 'wechat_user', + 'real_time_info' => 'wechat_real_time_info', + 'calculate_record' => 'cpp_calculate_record', + 'feedback' => 'cpp_feedback', + ], ]; - return isset($maps[$businessType]) ? $maps[$businessType] : $maps[self::BUSINESS_ADMIN]; } - /** - * 获取业务类型对应的Session标识 - * @param string $businessType 业务类型 - * @return string - */ public static function getSessionKey($businessType) { $keys = [ self::BUSINESS_ADMIN => 'admin_user_id', self::BUSINESS_ADMINGHD => 'adminghd_user_id', + self::BUSINESS_ADMINCPP => 'admincpp_user_id', ]; - return isset($keys[$businessType]) ? $keys[$businessType] : 'admin_user_id'; } - /** - * 获取业务类型对应的登录时间Session标识 - * @param string $businessType 业务类型 - * @return string - */ public static function getLoginTimeKey($businessType) { $keys = [ self::BUSINESS_ADMIN => 'admin_user_login_time', self::BUSINESS_ADMINGHD => 'adminghd_user_login_time', + self::BUSINESS_ADMINCPP => 'admincpp_user_login_time', ]; - return isset($keys[$businessType]) ? $keys[$businessType] : 'admin_user_login_time'; } - /** - * 根据当前模块名获取业务类型 - * @return string - */ public static function getBusinessTypeByModule() { $module = request()->module(); - return $module === 'adminghd' ? self::BUSINESS_ADMINGHD : self::BUSINESS_ADMIN; + if ($module === 'adminghd') return self::BUSINESS_ADMINGHD; + if ($module === 'admincpp') return self::BUSINESS_ADMINCPP; + return self::BUSINESS_ADMIN; } - /** - * 获取数据表名 - * @param string $tableKey 表键名(user, real_time_info, calculate_record) - * @param string|null $businessType 业务类型,为空则自动检测 - * @return string - */ public static function getTableName($tableKey, $businessType = null) { if ($businessType === null) { $businessType = self::getBusinessTypeByModule(); } - $map = self::getTableMap($businessType); return isset($map[$tableKey]) ? $map[$tableKey] : $tableKey; } diff --git a/后台文档.md b/后台文档.md deleted file mode 100644 index a302f8f..0000000 --- a/后台文档.md +++ /dev/null @@ -1,822 +0,0 @@ -# 后台管理系统文档 - -**项目名称:** 瑞莱医疗后台管理系统 -**技术架构:** ThinkPHP 5.0 + Layui + jQuery -**数据库:** MySQL -**文档版本:** v2.0 -**创建日期:** 2025年1月28日 -**最后更新:** 2025年1月29日(已完成逻辑彻底分离) - -> **✅ 分离状态:** 两个后台模块已实现**逻辑彻底分离**(方案一),可独立维护,互不影响。 -> - **admin 模块**:早熟预测后台,完全独立实现 -> - **adminghd 模块**:GHD预测后台,完全独立实现 -> - 两个模块不再依赖 `common` 模块的业务基类 - ---- - -## 一、系统概述 - -### 1.1 系统简介 - -本系统为两个医疗小程序提供统一的后台管理服务: - -1. **女童成长发育早熟预测模型后台** (`admin` 模块) - - 对应小程序:瑞莱医疗小程序 - - 小程序AppID: `wx0847525a15342a46` - - 访问路径:`/admin/` - -2. **生长激素缺乏预测模型后台** (`adminghd` 模块) - - 对应小程序:生长激素缺乏(GHD)预测模型小程序 - - 小程序AppID: `wxa75a76964ae7ce73` - - 访问路径:`/adminghd/` - -### 1.2 系统架构 - -``` -┌─────────────────────────────────────────┐ -│ 后台管理系统 │ -├─────────────────────────────────────────┤ -│ admin模块 adminghd模块 │ -│ (早熟预测) (GHD预测) │ -├─────────────────────────────────────────┤ -│ 共同功能: │ -│ - 登录认证 │ -│ - 菜单管理 │ -│ - 资讯管理 │ -│ - 用户管理 │ -│ - 计算记录管理 │ -└─────────────────────────────────────────┘ -``` - ---- - -## 二、系统架构 - -### 2.1 目录结构 - -``` -/www/wwwroot/code/ -├── application/ # 应用目录 -│ ├── admin/ # 早熟预测后台模块 -│ │ ├── controller/ # 控制器 -│ │ │ ├── Login.php # 登录控制器 -│ │ │ ├── Menu.php # 菜单管理 -│ │ │ ├── Wechatset.php # 资讯管理 -│ │ │ ├── Wechatinfro.php # 用户和记录管理 -│ │ │ └── Dashboard.php # 仪表盘 -│ │ ├── common/ # 公共类 -│ │ │ └── Base.php # 基础控制器(登录验证) -│ │ └── view/ # 视图模板 -│ │ ├── login/ # 登录相关视图 -│ │ │ ├── login.html # 登录页 -│ │ │ └── nav.html # 主框架页 -│ │ ├── wechatset/ # 资讯管理视图 -│ │ └── wechatinfro/ # 用户和记录视图 -│ ├── adminghd/ # GHD预测后台模块 -│ │ ├── controller/ # 控制器(结构同admin) -│ │ ├── common/ # 公共类 -│ │ └── view/ # 视图模板 -│ ├── app/ # 小程序API模块 -│ │ └── controller/ -│ │ ├── Ruilaiwechat.php # 瑞莱小程序API -│ │ └── Ghdwechat.php # GHD小程序API -│ └── common/ # 公共模块 -│ ├── config/ # 配置类 -│ │ └── BusinessConfig.php # 业务配置 -│ └── controller/ # 公共控制器 -│ ├── WechatsetBase.php # 资讯管理基类 -│ ├── WechatinfroBase.php # 用户管理基类 -│ └── DashboardBase.php # 仪表盘基类 -├── public/ # 公共访问目录 -│ ├── static/ # 静态资源 -│ │ ├── admin/ # admin模块静态资源 -│ │ │ ├── css/ # 样式文件 -│ │ │ ├── js/ # JavaScript文件 -│ │ │ └── layui/ # Layui框架 -│ │ └── adminghd/ # adminghd模块静态资源 -│ └── index.php # 入口文件 -└── thinkphp/ # ThinkPHP框架核心 -``` - -### 2.2 技术栈 - -- **后端框架:** ThinkPHP 5.0 -- **前端框架:** Layui 2.x -- **JavaScript库:** jQuery -- **数据库:** MySQL -- **会话管理:** ThinkPHP Session - ---- - -## 三、模块说明 - -### 3.1 admin 模块(早熟预测后台) - -#### 3.1.1 访问路径 -- 登录页:`/admin/login` -- 主框架:`/admin/login/index`(登录后自动跳转) - -#### 3.1.2 Session配置 -- 用户ID键名:`admin_user_id` -- 登录时间键名:`admin_user_login_time` -- 超时时间:3600秒(1小时) - -#### 3.1.3 数据表映射 -| 业务表 | 数据库表名 | -|--------|-----------| -| 用户表 | `wechat_user` | -| 资讯表 | `wechat_real_time_info` | -| 计算记录表 | `wechat_calculate_record` | -| 菜单表 | `menu` | -| 管理员表 | `user` | - -#### 3.1.4 静态资源路径 -- CSS:`/static/admin/css/` -- JS:`/static/admin/js/` -- Layui:`/static/admin/layui/` - -### 3.2 adminghd 模块(GHD预测后台) - -#### 3.2.1 访问路径 -- 登录页:`/adminghd/login` -- 主框架:`/adminghd/login/index`(登录后自动跳转) - -#### 3.2.2 Session配置 -- 用户ID键名:`adminghd_user_id` -- 登录时间键名:`adminghd_user_login_time` -- 超时时间:3600秒(1小时) - -#### 3.2.3 数据表映射 -| 业务表 | 数据库表名 | -|--------|-----------| -| 用户表 | `ghd_wechat_user` | -| 资讯表 | `wechat_real_time_info` | -| 计算记录表 | `wechat_calculate_record` | -| 菜单表 | `menu` | -| 管理员表 | `user` | - -#### 3.2.4 静态资源路径 -- CSS:`/static/adminghd/css/` -- JS:`/static/adminghd/js/` -- Layui:`/static/adminghd/layui/` - ---- - -## 四、功能模块 - -### 4.1 登录认证模块 - -#### 4.1.1 登录功能 - -**控制器:** `Login.php` - -**登录流程:** -1. 用户访问登录页 `/admin/login` 或 `/adminghd/login` -2. 输入用户名(手机号)和密码 -3. 系统验证用户信息: - - 查询 `user` 表 - - 验证条件:`phone` = 用户名,`password` = MD5(密码),`status` = 1,`type` = 1 -4. 登录成功: - - 设置Session:`admin_user_id` 或 `adminghd_user_id` - - 设置登录时间:`admin_user_login_time` 或 `adminghd_user_login_time`(当前时间 + 3600秒) - - 返回JSON:`{'status':1, 'msg':'登录成功', 'token':1}` -5. 跳转到主框架页面 - -**登录验证:** -- 所有需要登录的页面继承 `Base` 类 -- `Base::_initialize()` 方法自动检查Session -- 未登录或超时:跳转到登录页 - -#### 4.1.2 退出功能 - -**接口:** `/admin/Login/logout` 或 `/adminghd/Login/logout` - -**功能:** -- 清除所有Session数据 -- 返回JSON:`{'status':1, 'msg':'退出成功'}` -- 前端跳转到登录页 - -### 4.2 菜单管理模块 - -#### 4.2.1 菜单结构 - -**数据表:** `menu` - -**表结构:** -- `id`:菜单ID(主键) -- `pid`:父菜单ID(空或0表示一级菜单) -- `menu_name`:菜单名称 -- `url`:菜单链接 -- `seq_on`:排序序号 -- `menu_icon`:菜单图标 -- `create_time`:创建时间 - -**菜单层级:** -- 一级菜单:`pid` 为空或0 -- 二级菜单:`pid` 指向一级菜单的 `id` - -#### 4.2.2 获取菜单列表 - -**接口:** `/admin/Menu/getMenuList` 或 `/adminghd/Menu/getMenuList` - -**返回格式:** -```json -{ - "status": 1, - "msg": "获取数据成功!!", - "data": [ - { - "id": "xxx", - "pid": "", - "menu_name": "小程序设置", - "url": "", - "seq_on": 1, - "type": 1, - "children": [ - { - "id": "yyy", - "pid": "xxx", - "menu_name": "首页资讯列表", - "url": "/admin/Wechatset/wechatRealTimeInfo", - "type": 2 - } - ] - } - ] -} -``` - -#### 4.2.3 菜单管理功能 - -- **添加菜单:** `/admin/Menu/doaddMenu` -- **修改菜单:** `/admin/Menu/doupdMenu` -- **删除菜单:** `/admin/Menu/delMenu` -- **获取菜单详情:** `/admin/Menu/updMenu` - -### 4.3 用户信息模块 - -#### 4.3.1 获取当前用户信息 - -**接口:** `/admin/Menu/getUserInfor` 或 `/adminghd/Menu/getUserInfor` - -**返回格式:** -```json -{ - "status": 1, - "msg": "查询成功", - "infro": { - "user_name": "管理员", - "user_head": "/static/admin/img/upload.png" - } -} -``` - -#### 4.3.2 小程序注册用户管理 - -**控制器:** `Wechatinfro.php` - -**功能列表:** -- **用户列表:** `/admin/Wechatinfro/wechatUserList` -- **获取用户列表:** `/admin/Wechatinfro/getWechatUserList` - -**数据表:** -- admin模块:`wechat_user` -- adminghd模块:`ghd_wechat_user` - -**用户列表字段:** -- `uid`:用户ID -- `nickname`:用户昵称 -- `headimg`:头像 -- `create_time`:注册时间 - -**搜索功能:** -- 支持按昵称模糊搜索 - -### 4.4 资讯管理模块 - -#### 4.4.1 资讯列表 - -**控制器:** `Wechatset.php` - -**功能列表:** -- **资讯列表页:** `/admin/Wechatset/wechatRealTimeInfo` -- **获取资讯列表:** `/admin/Wechatset/getWechatRealTimeInfoList` -- **添加资讯页:** `/admin/Wechatset/wechatRealTimeInfoAdd` -- **编辑资讯页:** `/admin/Wechatset/wechatRealTimeInfoUpdate` -- **保存资讯:** `/admin/Wechatset/saveWechatRealTimeInfo` -- **删除资讯:** `/admin/Wechatset/delWechatRealTimeInfo` -- **获取资讯详情:** `/admin/Wechatset/getRealTimeInfoDetail` - -**数据表:** `wechat_real_time_info`(两个模块共用) - -**资讯字段:** -- `id`:资讯ID -- `title_plain`:标题 -- `thumbnail`:缩略图 -- `excerpt_plain`:简介 -- `url`:跳转链接 -- `create_time`:创建时间 - -**搜索功能:** -- 支持按标题模糊搜索 - -#### 4.4.2 资讯管理流程 - -1. **添加资讯:** - - 填写标题、上传图片、填写简介、填写跳转链接 - - 系统自动生成ID(MD5(时间戳+随机数)) - - 保存到数据库 - -2. **编辑资讯:** - - 根据ID获取资讯详情 - - 修改后保存 - -3. **删除资讯:** - - 根据ID删除记录 - -### 4.5 计算记录管理模块 - -#### 4.5.1 计算记录列表 - -**控制器:** `Wechatinfro.php` - -**功能列表:** -- **记录列表页:** `/admin/Wechatinfro/wechatRecordList` -- **获取记录列表:** `/admin/Wechatinfro/getWechatRecordList` - -**数据表:** `wechat_calculate_record`(两个模块共用) - -**记录字段:** -- `id`:记录ID -- `name`:姓名 -- `age`:年龄 -- `height`:身高 -- `bone_age`:骨龄 -- `father_height`:父亲身高 -- `mother_height`:母亲身高 -- `IGF`:IGF-1值(GHD)或IGFBP-3值(早熟) -- `LH`:LH值(早熟) -- `uterus_thickness`:子宫厚度(早熟) -- `calculate_resutlt`:计算结果 -- `create_time`:创建时间 - -**搜索功能:** -- 支持按姓名模糊搜索 - -### 4.6 仪表盘模块(Dashboard) - -#### 4.6.1 统计概览 - -**控制器:** `Dashboard.php` - -**功能列表:** -- **仪表盘页面:** `/admin/Dashboard/index` -- **获取统计数据:** `/admin/Dashboard/getStatistics` - -**统计数据:** -- 注册用户数 -- 首页资讯数量 -- 最近一次计算时间 -- 今日新增用户数 -- 总计算次数 - ---- - -## 五、数据库设计 - -### 5.1 核心数据表 - -#### 5.1.1 管理员表(user) - -**说明:** 后台管理员账户表,两个模块共用 - -**字段:** -- `id`:管理员ID(主键) -- `user_name`:管理员姓名 -- `phone`:手机号(登录用户名) -- `password`:密码(MD5加密) -- `user_head`:头像 -- `status`:状态(1=启用,0=禁用) -- `type`:类型(1=管理员) - -#### 5.1.2 菜单表(menu) - -**说明:** 后台菜单配置表,两个模块共用 - -**字段:** -- `id`:菜单ID(主键,MD5生成) -- `pid`:父菜单ID(空或0=一级菜单) -- `menu_name`:菜单名称 -- `url`:菜单链接 -- `seq_on`:排序序号 -- `menu_icon`:菜单图标 -- `create_time`:创建时间 - -#### 5.1.3 小程序用户表 - -**说明:** 小程序注册用户表,两个模块使用不同的表 - -**admin模块:** `wechat_user` -**adminghd模块:** `ghd_wechat_user` - -**字段:** -- `uid`:用户ID(主键) -- `openid`:微信OpenID -- `nickname`:用户昵称 -- `headimg`:头像 -- `create_time`:注册时间 - -#### 5.1.4 资讯表(wechat_real_time_info) - -**说明:** 首页资讯表,两个模块共用 - -**字段:** -- `id`:资讯ID(主键,MD5生成) -- `title_plain`:标题 -- `thumbnail`:缩略图URL -- `excerpt_plain`:简介 -- `url`:跳转链接 -- `create_time`:创建时间 - -#### 5.1.5 计算记录表(wechat_calculate_record) - -**说明:** 预测计算记录表,两个模块共用 - -**字段:** -- `id`:记录ID(主键,MD5生成) -- `uid`:用户ID -- `name`:姓名 -- `age`:年龄 -- `height`:身高(cm) -- `bone_age`:骨龄(年) -- `father_height`:父亲身高(cm) -- `mother_height`:母亲身高(cm) -- `IGF`:IGF-1值(GHD)或IGFBP-3值(早熟) -- `LH`:LH值(早熟预测) -- `uterus_thickness`:子宫厚度(早熟预测) -- `calculate_resutlt`:计算结果(JSON格式) -- `create_time`:创建时间 - ---- - -## 六、前端架构 - -### 6.1 主框架结构 - -**文件:** `application/admin/view/login/nav.html` 或 `application/adminghd/view/login/nav.html` - -**布局:** -- 左侧导航栏:菜单列表 -- 顶部栏:面包屑导航 + 用户信息 + 退出按钮 -- 主内容区:iframe加载功能页面 - -### 6.2 导航菜单渲染 - -**文件:** `public/static/admin/js/nav.js` 或 `public/static/adminghd/js/nav.js` - -**功能:** -1. 页面加载时调用 `/admin/Menu/getMenuList` 获取菜单数据 -2. 将菜单数据渲染为左侧导航栏 -3. 支持一级和二级菜单结构 -4. 点击菜单项,在iframe中加载对应页面 - -### 6.3 样式框架 - -**CSS框架:** Layui 2.x - -**主要样式文件:** -- `nav.css`:主框架样式 -- `login.css`:登录页样式 -- `index.css`:列表页样式 - ---- - -## 七、API接口 - -### 7.1 登录相关 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Login/index` | GET | 登录页 | -| `/admin/Login/login` | POST | 登录验证 | -| `/admin/Login/logout` | POST | 退出登录 | -| `/admin/Login/logoutJump` | GET | 超时跳转页 | - -### 7.2 菜单相关 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Menu/getMenuList` | POST | 获取菜单列表 | -| `/admin/Menu/getUserInfor` | POST | 获取当前用户信息 | -| `/admin/Menu/doaddMenu` | POST | 添加菜单 | -| `/admin/Menu/doupdMenu` | POST | 修改菜单 | -| `/admin/Menu/delMenu` | POST | 删除菜单 | - -### 7.3 用户管理 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Wechatinfro/wechatUserList` | GET | 用户列表页 | -| `/admin/Wechatinfro/getWechatUserList` | POST | 获取用户列表 | - -### 7.4 资讯管理 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Wechatset/wechatRealTimeInfo` | GET | 资讯列表页 | -| `/admin/Wechatset/getWechatRealTimeInfoList` | POST | 获取资讯列表 | -| `/admin/Wechatset/wechatRealTimeInfoAdd` | GET | 添加资讯页 | -| `/admin/Wechatset/wechatRealTimeInfoUpdate` | GET | 编辑资讯页 | -| `/admin/Wechatset/saveWechatRealTimeInfo` | POST | 保存资讯 | -| `/admin/Wechatset/delWechatRealTimeInfo` | POST | 删除资讯 | -| `/admin/Wechatset/getRealTimeInfoDetail` | POST | 获取资讯详情 | - -### 7.5 计算记录 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Wechatinfro/wechatRecordList` | GET | 记录列表页 | -| `/admin/Wechatinfro/getWechatRecordList` | POST | 获取记录列表 | - -### 7.6 仪表盘 - -| 接口 | 方法 | 说明 | -|------|------|------| -| `/admin/Dashboard/index` | GET | 仪表盘页面 | -| `/admin/Dashboard/getStatistics` | POST | 获取统计数据 | - ---- - -## 八、安全机制 - -### 8.1 登录验证 - -- 所有需要登录的控制器继承 `Base` 类 -- `Base::_initialize()` 自动检查Session -- 未登录或超时自动跳转到登录页 - -### 8.2 密码加密 - -- 密码使用MD5加密存储 -- 登录时对输入密码进行MD5加密后比对 - -### 8.3 Session管理 - -- 登录成功设置Session,有效期1小时 -- 每次请求检查Session是否过期 -- 退出登录清除所有Session - -### 8.4 权限控制 - -- 当前版本未实现细粒度权限控制 -- 所有登录用户拥有相同权限 -- 预留权限控制接口(代码中已注释) - ---- - -## 九、部署说明 - -### 9.1 环境要求 - -- PHP >= 5.6 -- MySQL >= 5.6 -- Apache/Nginx Web服务器 -- ThinkPHP 5.0框架 - -### 9.2 配置说明 - -#### 9.2.1 数据库配置 - -**文件:** `application/database.php` - -```php -return [ - 'type' => 'mysql', - 'hostname' => '127.0.0.1', - 'database' => 'ruilai', - 'username' => 'rootrui', - 'password' => 'X2)jB+k%YH.p', - 'charset' => 'utf8', - 'prefix' => 't_sys_', -]; -``` - -#### 9.2.2 路由配置 - -**文件:** `application/route.php` - -默认使用ThinkPHP的路由规则,URL格式: -- `/模块/控制器/方法` - -例如: -- `/admin/Login/index` → `application/admin/controller/Login.php::index()` - -### 9.3 静态资源 - -静态资源存放在 `public/static/` 目录下,通过Web服务器直接访问。 - -**访问路径:** -- `/static/admin/css/nav.css` -- `/static/adminghd/js/nav.js` - ---- - -## 十、常见问题 - -### 10.1 登录问题 - -**问题:** 登录后立即跳转到登录页 - -**原因:** Session未正确设置或Base类验证失败 - -**解决:** -1. 检查Session配置 -2. 检查Base类的Session键名是否正确 -3. 检查数据库用户表数据 - -### 10.2 菜单不显示 - -**问题:** 左侧导航栏菜单不显示 - -**原因:** -1. 菜单数据未正确返回 -2. JavaScript渲染错误 -3. 菜单表数据为空 - -**解决:** -1. 检查浏览器控制台错误 -2. 检查 `/admin/Menu/getMenuList` 接口返回 -3. 检查 `menu` 表是否有数据 - -### 10.3 数据表不存在 - -**问题:** 提示数据表不存在 - -**原因:** 数据库表前缀配置错误 - -**解决:** -1. 检查 `application/database.php` 中的 `prefix` 配置 -2. 确认数据库表名是否正确(包含前缀) - ---- - -## 十一、开发规范 - -### 11.1 代码规范 - -- 遵循PSR-2编码规范 -- 控制器类名首字母大写 -- 方法名使用驼峰命名 -- 注释使用PHPDoc格式 - -### 11.2 命名规范 - -- **控制器:** 大驼峰,如 `Wechatset.php` -- **方法:** 小驼峰,如 `getWechatUserList()` -- **数据表:** 小写下划线,如 `wechat_user` -- **Session键:** 小写下划线,如 `admin_user_id` - -### 11.3 文件组织 - -- 控制器放在 `controller/` 目录 -- 视图放在 `view/` 目录 -- 静态资源放在 `public/static/` 目录 -- 公共类放在 `common/` 目录 - ---- - -## 十二、后续优化建议 - -### 12.1 代码优化 - -1. **统一公共逻辑:** 将两个模块的公共代码提取到 `common` 模块 -2. **配置统一管理:** 使用配置文件管理业务类型和表映射 -3. **错误处理:** 统一异常处理和错误返回格式 - -### 12.2 功能增强 - -1. **权限管理:** 实现基于角色的权限控制(RBAC) -2. **操作日志:** 记录管理员操作日志 -3. **数据统计:** 增加更详细的数据统计和报表功能 -4. **批量操作:** 支持批量删除、批量导出等功能 - -### 12.3 性能优化 - -1. **缓存机制:** 菜单数据、统计数据使用缓存 -2. **数据库优化:** 添加索引,优化查询语句 -3. **前端优化:** 压缩静态资源,使用CDN - ---- - -## 十三、后台模块彻底分离维护方案 - -两个后台(admin 早熟预测、adminghd GHD 预测)可以做到**彻底分离维护**,互不影响。根据目标不同,有两种做法。 - -### 13.1 分离状态 - -**✅ 已完成逻辑彻底分离(方案一)** - -| 模块 | 状态 | 说明 | -|------|------|------| -| **admin 模块** | ✅ 已独立 | 所有控制器继承 `app\admin\common\Base`,不再依赖 common 模块 | -| **adminghd 模块** | ✅ 已独立 | 所有控制器继承 `app\adminghd\common\Base`,不再依赖 common 模块 | -| **Dashboard** | ✅ 已独立 | 两个模块的 `Dashboard` 控制器各自实现 `getStatistics` 方法,使用固定的表名 | -| **数据库** | 共用 | 同一库 `ruilai`,同一前缀 `t_sys_`;部分表共用(menu、wechat_real_time_info、wechat_calculate_record),用户表不同(wechat_user / ghd_wechat_user) | -| **入口与框架** | 共用 | 同一 `public/index.php`、同一 ThinkPHP 应用目录 | - -**已解除的耦合:** -- ✅ `Dashboard` 不再继承 `DashboardBase` -- ✅ 表名和 Session 键已硬编码到各自控制器中 -- ✅ 两个模块可独立修改,互不影响 - ---- - -### 13.2 方案一:同仓库内逻辑彻底分离(✅ 已采用) - -**目标**:同一套代码、同一部署,但 admin 与 adminghd 在代码上**完全独立**,改一个模块不会牵动另一个,也不依赖 common 里的业务基类。 - -**实施状态:** ✅ **已完成** - -**已完成的修改:** - -1. **✅ 解除 Dashboard 对 common 的依赖** - - `application/admin/controller/Dashboard.php` 已改为继承 `\app\admin\common\Base`,不再继承 `DashboardBase`。 - - `getStatistics` 方法已独立实现,使用固定的表名 `wechat_user`、`wechat_real_time_info`、`wechat_calculate_record`。 - - `application/adminghd/controller/Dashboard.php` 已改为继承 `\app\adminghd\common\Base`,使用固定的表名 `ghd_wechat_user`、`wechat_real_time_info`、`wechat_calculate_record`。 - -2. **✅ 代码独立性验证** - - 两个模块的控制器已完全独立,不再依赖 `app\common\controller\*Base` 和 `BusinessConfig`。 - - 表名和 Session 键已硬编码到各自控制器中。 - -3. **✅ common 模块保留** - - `common` 模块中的基类(`DashboardBase`、`WechatinfroBase`、`WechatsetBase`)已保留,但不再被使用。 - - 可作为参考或未来新后台的模板,但不影响现有两个模块的独立性。 - -**优点**:✅ 部署不变、数据库不变,只改一处不影响另一处,适合继续同仓库、同服务器维护。 -**维护约定**:两个模块独立维护,如需同步功能,需分别在两个模块中实现。建议在代码注释中标注需要同步的功能点。 - ---- - -### 13.3 方案二:拆成两个独立项目(物理分离) - -**目标**:两套独立代码库、可独立部署、独立发版,甚至不同服务器、不同数据库。 - -**思路**: -- 以当前代码为基准,复制出两份项目:一份只保留 admin 相关(及 app、index 等必要模块),一份只保留 adminghd 相关。 -- 每份项目有独立 `application`、独立入口(可共用同一 `public` 或各建各的)、独立配置与依赖。 - -**具体步骤**: - -1. **项目 A(早熟预测后台)** - - 新建目录如 `code-admin`,保留 ThinkPHP 框架、`public`、`application/database.php` 等。 - - 只保留 `application/admin`、`application/app`(瑞莱小程序 API)、`application/index` 等与早熟业务相关的模块;删除 `application/adminghd` 和 `application/common` 中业务相关部分。 - - 将原 admin 内对表名、Session 的写法固定为早熟业务(wechat_user、admin_user_id 等)。 - - 配置独立入口(如 `code-admin/public/index.php`)和独立域名或路径。 - -2. **项目 B(GHD 预测后台)** - - 新建目录如 `code-adminghd`,同样保留框架与必要目录。 - - 只保留 `application/adminghd`、`application/app`(GHD 小程序 API)等;删除 `application/admin` 和 common 业务基类。 - - 表名、Session 固定为 GHD(ghd_wechat_user、adminghd_user_id 等)。 - - 配置独立入口与访问方式。 - -3. **数据库** - - **选项甲**:仍用同一库,两项目连同一 `database.php`,仅模块不同、访问表不同。 - - **选项乙**:分库(如 `ruilai` 与 `ruilai_ghd`),各项目独立 `database.php`,表结构可复制或迁移脚本分表。 - -4. **静态资源** - - 各项目 `public/static` 下只保留本后台所需(如项目 A 只保留 `admin`,项目 B 只保留 `adminghd`),避免混用。 - -**优点**:版本、上线、回滚完全独立,技术栈升级可分批进行。 -**缺点**:两套代码、两套部署与运维;公共 bug 或需求要改两处,需通过复制或少量共享库(如 composer 私有包)来收敛。 - ---- - -### 13.4 选择建议 - -| 场景 | 建议 | -|------|------| -| 希望**少动部署、少动库**,只希望开发时互不干扰 | 采用 **方案一**(同仓库逻辑分离) | -| 需要**独立域名、独立服务器、独立发版节奏** | 采用 **方案二**(拆成两个项目) | -| 未来可能新增更多“类似后台”且希望复用一套基类 | 可保留 common 基类,仅把 admin、adminghd 改为不继承它们,实现“可选复用、默认分离” | - -**实施完成日期:** 2025年1月29日 -**采用的方案:** 方案一(同仓库内逻辑彻底分离) -**状态:** ✅ 已完成,两个模块已完全独立,可独立维护 - ---- - -## 附录 - -### A. 相关文档 - -- [数据库配置文档](./数据库配置.md) -- [生长激素缺乏预测模型项目需求文档](./生长激素缺乏预测模型项目需求文档.md) - -### B. 联系方式 - -如有问题,请联系开发团队。 - ---- - -**文档生成时间:** 2025年1月28日 -**最后更新:** 2025年1月28日 diff --git a/后台架构文档.md b/后台架构文档.md new file mode 100644 index 0000000..0de9c26 --- /dev/null +++ b/后台架构文档.md @@ -0,0 +1,457 @@ +# CPP 生长减速预测模型 — 后台架构文档 + +## 一、系统总览 + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ 客户端 (微信小程序) │ +│ ├── szjs (生长激素缺乏 GHD) → 微信AppID: wx... │ +│ ├── cppszjs (CPP 生长减速) → 微信AppID: wx0847525a15342a46 │ +│ └── rlgz (瑞来工作) → 微信AppID: wx... │ +└──────────────────────────────┬───────────────────────────────────────┘ + │ HTTPS POST + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ ThinkPHP 5.0 后端 (ruilaizipj.com) — 腾讯云 101.43.95.130 │ +│ │ +│ app 模块 (小程序API) │ +│ ├── Cppwechat.php ← cppszjs 小程序 API │ +│ ├── Ruilaiwechat.php ← szjs 小程序 API │ +│ └── ... │ +│ │ +│ 后台管理模块 (三套独立入口) │ +│ ├── admincpp/ → /admincpp CPP 生长减速预测模型后台 │ +│ ├── adminghd/ → /adminghd 生长激素缺乏预测模型后台 │ +│ └── admin/ → /admin 通用后台 │ +└──────────────────────────────┬───────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ MySQL 数据库 (127.0.0.1:3306, 库名: ruilai) │ +│ 表前缀: t_sys_ │ +│ 用户名: rootrui 密码: X2)jB+k%YH.p │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 二、URL 路由与模块隔离 + +### 2.1 模块自动识别 + +ThinkPHP 通过 URL 第一段自动识别模块名,由 `BusinessConfig.php` 实现: + +```php +// /application/common/config/BusinessConfig.php +public static function getBusinessTypeByModule() { + $module = request()->module(); + if ($module === 'adminghd') return self::BUSINESS_ADMINGHD; + if ($module === 'admincpp') return self::BUSINESS_ADMINCPP; + return self::BUSINESS_ADMIN; +} +``` + +### 2.2 三个后台入口 + +| 模块 | URL | Session Key | 对应项目 | +|------|-----|-------------|----------| +| admin | /admin/login | admin_user_id | 通用 | +| adminghd | /adminghd/login | adminghd_user_id | 生长激素缺乏 GHD | +| admincpp | /admincpp/login | admincpp_user_id | CPP 生长减速 | + +### 2.3 表名映射 + +不同模块访问同一逻辑表名时,自动路由到不同的物理表: + +| 逻辑表名 | admin | adminghd | admincpp | +|----------|-------|----------|----------| +| user | wechat_user | ghd_wechat_user | wechat_user | +| calculate_record | wechat_calculate_record | wechat_calculate_record | cpp_calculate_record | +| feedback | — | — | cpp_feedback | +| real_time_info | wechat_real_time_info | wechat_real_time_info | wechat_real_time_info | + +> 注:ThinkPHP `Db::name('xxx')` 会自动添加 `t_sys_` 前缀,例如 `Db::name('wechat_user')` → `t_sys_wechat_user` + +--- + +## 三、admincpp 模块 — 目录结构 + +``` +application/admincpp/ +├── common/ +│ └── Base.php # 基类 (session 登录校验) +├── common.php # 模块公共函数 +├── config.php # 模块配置 +├── controller/ +│ ├── Dashboard.php # 仪表盘 / 统计 +│ ├── Files.php # 文件上传 +│ ├── Index.php # 首页 / 菜单管理 API +│ ├── Login.php # 登录 / 退出 / 超时跳转 +│ ├── Menu.php # 菜单管理 (CRUD) +│ ├── Wechatinfro.php # 用户列表 / 计算记录 / 反馈列表 +│ └── Wechatset.php # 首页资讯管理 +├── model/ +│ ├── Permission.php # 权限模型 +│ └── Role.php # 角色模型 +└── view/ + ├── dashboard/ + │ └── index.html # 系统概览页 (统计卡片) + ├── login/ + │ ├── login.html # 登录页 + │ ├── logoutJump.html # 超时跳转页 + │ └── nav.html # 主框架 (侧边栏+顶栏+iframe) + ├── wechatinfro/ + │ ├── feedbackList.html # 反馈列表 + │ ├── userList.html # 用户列表 + │ └── wechatRecordList.html# CPP 计算记录列表 + └── wechatset/ + ├── realTimeInfo.html # 资讯列表 + ├── realTimeInfoAdd.html # 添加资讯 + └── realTimeInfoUpdate.html # 编辑资讯 + +public/static/admincpp/ +├── css/ +│ ├── activity.css # 活动页样式 +│ ├── index.css # 内容页样式 (table, main, head) +│ ├── login.css # 登录页样式 +│ ├── nav.css # 主框架样式 (侧边栏+顶栏) +│ └── style.css # 全局基础样式 (reset) +├── js/ +│ ├── common.js # 公共函数 (超时检测) +│ ├── jquery.js # jQuery +│ ├── menu.js # (废弃) 早期菜单组件 +│ ├── nav.js # 主框架JS (菜单渲染、用户信息、退出) +│ ├── wechatinfro/ +│ │ ├── feedbackList.js # 反馈列表 DataTable +│ │ ├── userList.js # 用户列表 DataTable +│ │ └── wechatRecordList.js # 计算记录 DataTable +│ └── wechatset/ +│ ├── wechatRealTimeInfoAdd.js # 添加资讯 +│ ├── wechatRealTimeInfoList.js # 资讯列表 +│ └── wechatRealTimeInfoUpdate.js # 编辑资讯 +├── img/ +│ └── upload.png # 默认头像 +└── layui/ # layUI 框架 (v2.x) + ├── css/ + ├── font/ + ├── images/ + ├── lay/ + └── layui.all.js +``` + +--- + +## 四、控制器详解 + +### 4.1 Login.php — 登录控制器 + +| 方法 | URL | Method | 说明 | +|------|-----|--------|------| +| index() | /admincpp/Login/index | GET | 渲染登录页 (未登录) 或导航框架 (已登录) | +| login() | /admincpp/Login/login | POST | 验证手机号+MD5密码,写入 session | +| logout() | /admincpp/Login/logout | POST | 清除 session,退出登录 | +| logoutJump() | /admincpp/Login/logoutJump | GET | 超时跳转提示页 | + +**登录验证流程**: +1. 查询 `t_sys_user` 表:`phone=$username AND password=MD5($password) AND status=1 AND type=1` +2. 验证通过后设置 session: + - `admincpp_user_id` → 用户ID + - `admincpp_user_login_time` → `time() + 3600` + +### 4.2 Dashboard.php — 仪表盘 + +| 方法 | URL | Method | 说明 | +|------|-----|--------|------| +| index() | /admincpp/Dashboard/index | GET | 渲染系统概览页 | +| getStatistics() | /admincpp/Dashboard/getStatistics | POST | 返回 JSON 统计数据 | + +**统计返回字段**: +```json +{ + "status": 1, + "data": { + "user_count": 87, // 微信用户总数 + "today_user_count": 0, // 今日新增 + "total_calculate_count": 19, // 累计计算次数 + "info_count": 2, // 首页资讯数量 + "last_calculate_time": "2026-07-18 15:37:53" + } +} +``` + +### 4.3 Wechatinfro.php — 数据管理 + +| 方法 | URL | Method | 说明 | +|------|-----|--------|------| +| wechatUserList() | /admincpp/Wechatinfro/wechatUserList | GET | 用户列表页 | +| getWechatUserList() | /admincpp/Wechatinfro/getWechatUserList | POST | 用户列表 JSON | +| wechatRecordList() | /admincpp/Wechatinfro/wechatRecordList | GET | 计算记录页 | +| getWechatRecordList() | /admincpp/Wechatinfro/getWechatRecordList | POST | 计算记录 JSON | +| feedbackList() | /admincpp/Wechatinfro/feedbackList | GET | 反馈列表页 | +| getFeedbackList() | /admincpp/Wechatinfro/getFeedbackList | POST | 反馈列表 JSON | + +**计算记录列表字段** (对应 `t_sys_cpp_calculate_record`): +`id, name, bone_age, uterus_length, uterus_width, lh_peak, fsh_peak, fsh_3m, igf1, calculate_result, risk_level, create_time` + +### 4.4 Wechatset.php — 资讯管理 + +| 方法 | URL | Method | 说明 | +|------|-----|--------|------| +| wechatRealTimeInfo() | /admincpp/Wechatset/wechatRealTimeInfo | GET | 资讯列表页 | +| wechatRealTimeInfoAdd() | /admincpp/Wechatset/wechatRealTimeInfoAdd | GET | 添加资讯页 | +| wechatRealTimeInfoUpdate() | /admincpp/Wechatset/wechatRealTimeInfoUpdate | GET | 编辑资讯页 | +| getWechatRealTimeInfoList() | /admincpp/Wechatset/getWechatRealTimeInfoList | POST | 资讯列表 JSON | +| saveWechatRealTimeInfo() | /admincpp/Wechatset/saveWechatRealTimeInfo | POST | 保存/更新资讯 | +| delWechatRealTimeInfo() | /admincpp/Wechatset/delWechatRealTimeInfo | POST | 删除资讯 | +| getRealTimeInfoDetail() | /admincpp/Wechatset/getRealTimeInfoDetail | POST | 获取单条资讯详情 | + +### 4.5 Menu.php — 菜单管理 + +| 方法 | URL | 说明 | +|------|-----|------| +| getMenuList() | /admincpp/Menu/getMenuList | 获取菜单树 (一级+二级) | +| getUserInfor() | /admincpp/Menu/getUserInfor | 获取当前登录用户信息 | +| doaddMenu() | /admincpp/Menu/doaddMenu | 添加菜单项 | +| doupdMenu() | /admincpp/Menu/doupdMenu | 更新菜单项 | +| delMenu() | /admincpp/Menu/delMenu | 删除菜单项 | + +### 4.6 Base.php — 登录校验基类 + +```php +class Base extends Controller { + protected function _initialize() { + if (!session('admincpp_user_id')) { + // 未登录 → 跳转超时页 + return $this->error('您还未登录', url('Login/logoutJump')); + } + $log_time = session('admincpp_user_login_time') - time(); + if ($log_time < 0) { + // 超时 → 清除 session → 跳转 + Session::clear(); + return $this->error('登录超时', url('Login/logoutJump')); + } + } +} +``` + +Dashboard、Wechatinfro、Wechatset、Menu 均继承 Base,需登录后才能访问。 + +--- + +## 五、前端框架 (nav.js) 工作流程 + +### 5.1 页面加载流程 + +``` +Login 成功 → 设置 session → 跳转 /admincpp/Login/index + → Login::index() 检测 session → 返回 nav.html + → nav.html 加载 nav.css + nav.js + → nav.js 初始化: + 1. 设置默认 iframe 页面: /admincpp/Dashboard/index + 2. AJAX 调用 /admincpp/Menu/getMenuList 获取菜单数据 + 3. AJAX 调用 /admincpp/Menu/getUserInfor 获取用户信息 + 4. MenuHtml() 渲染侧边栏菜单 + 5. 点击菜单 → 更新 iframe src +``` + +### 5.2 菜单 URL 转换 + +数据库共享 `t_sys_menu` 表,菜单 URL 默认为 `/admin/...`。nav.js 在渲染时自动转换: + +```javascript +// nav.js 中的 URL 转换逻辑 +data-url="${(child.url || '').replace('/admin/', '/admincpp/')}" +``` + +### 5.3 主框架布局 + +``` +┌──────────────┬────────────────────────────────────┐ +│ .layui-side │ .top_right │ +│ (侧边栏导航) │ ┌ .header_title ──┬ .user_right ┐ │ +│ │ │ 标题 │ 用户名 退出 │ │ +│ .layui-side- │ └─────────────────┴─────────────┘ │ +│ scroll │ │ +│ │ .main_content │ +│ ul#nav │ ┌──────────────────────────────┐ │ +│ (菜单树) │ │ iframe#iframeId │ │ +│ │ │ (加载各功能页面) │ │ +│ │ │ │ │ +│ │ └──────────────────────────────┘ │ +└──────────────┴────────────────────────────────────┘ +``` + +--- + +## 六、小程序 API (app/Cppwechat.php) + +位于 `/application/app/controller/Cppwechat.php`,为 CPP 小程序提供后端接口。 + +### 6.1 API 列表 + +| 方法 | URL | 说明 | +|------|-----|------| +| selectRealtimeInfoList() | /app/Cppwechat/selectRealtimeInfoList | 首页资讯列表 | +| calculate() | /app/Cppwechat/calculate | CPP 生长减速预测计算 | +| selectRecordList() | /app/Cppwechat/selectRecordList | 计算历史记录 | +| deleteRecord() | /app/Cppwechat/deleteRecord | 删除计算记录 | +| submitFeedback() | /app/Cppwechat/submitFeedback | 提交意见反馈 | +| wechatLogin() | /app/Cppwechat/wechatLogin | 微信登录 | +| getToken() | /app/Cppwechat/getToken | 获取登录 Token | +| getUserBaseInfo() | /app/Cppwechat/getUserBaseInfo | 获取用户基本信息 | + +### 6.2 预测模型公式 + +``` +交互项 = 骨龄 × 子宫长 +比值项 = LH峰值 / FSH峰值 +log(FSH峰值) = ln(FSH峰值) + +logit(p) = -2.8457 + + 0.1198 × (骨龄 × 子宫长) + + 1.0411 × ln(FSH峰值) + - 0.4832 × 治疗3月FSH + - 0.0044 × 基础IGF-1 + + 0.4207 × (LH峰值 / FSH峰值) + - 0.8303 × 子宫宽 + +p = exp(logit) / (1 + exp(logit)) +``` + +### 6.3 输入参数 + +| 字段 | 说明 | 范围 | 单位 | +|------|------|------|------| +| bone_age | 初诊骨龄 | 5.0 – 13.0 | 岁 | +| uterus_length | 初诊子宫长 | 1.0 – 4.5 | cm | +| uterus_width | 初诊子宫宽 | 0.5 – 3.5 | cm | +| lh_peak | LH 峰值 | 0.5 – 90.0 | IU/L | +| fsh_peak | FSH 峰值 | 0.5 – 25.0 | IU/L | +| fsh_3m | 治疗3月FSH | 0.0 – 8.0 | IU/L | +| igf1 | 基础IGF-1 | 100 – 800 | ng/mL | + +### 6.4 风险分级 + +| 等级 | 概率范围 | 临床建议 | +|------|----------|----------| +| 低风险 | p < 0.430 | 继续目前 GnRHa 治疗方案,常规6个月随访 | +| 中风险 | 0.430 ≤ p ≤ 0.572 | 缩短随访间隔至3个月,关注GV下降趋势 | +| 高风险 | p > 0.572 | 每2-3个月密切随访,尽早考虑 rhGH 联合干预 | + +### 6.5 性能指标 + +- **AUC**: 0.718 (Bootstrap 矫正 0.687) +- **Hosmer-Lemeshow**: P = 0.909(校准优秀) + +--- + +## 七、数据库表结构 + +### 7.1 t_sys_cpp_calculate_record(计算记录) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | varchar(32) PK | MD5 唯一ID | +| uid | int(11) | 用户ID | +| name | varchar(32) | 患者姓名 | +| bone_age | decimal(5,2) | 初诊骨龄 | +| uterus_length | decimal(5,2) | 初诊子宫长 | +| uterus_width | decimal(5,2) | 初诊子宫宽 | +| lh_peak | decimal(6,2) | LH 峰值 | +| fsh_peak | decimal(6,2) | FSH 峰值 | +| fsh_3m | decimal(5,2) | 治疗3月 FSH | +| igf1 | decimal(7,2) | 基础 IGF-1 | +| calculate_result | decimal(6,4) | 预测概率 p | +| risk_level | varchar(10) | 风险等级 | +| create_time | datetime | 创建时间 | + +### 7.2 t_sys_cpp_feedback(意见反馈) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | int(11) PK AUTO_INCREMENT | 自增ID | +| uid | varchar(50) | 用户ID | +| content | text | 反馈内容 | +| create_time | datetime | 提交时间 | + +### 7.3 t_sys_wechat_user(微信用户) + +| 字段 | 类型 | 说明 | +|------|------|------| +| uid | int(11) PK AUTO_INCREMENT | 用户ID | +| openid | varchar(64) | 微信 OpenID | +| nickname | varchar(64) | 微信昵称 | +| headimg | varchar(200) | 头像URL | +| create_time | datetime | 注册时间 | + +### 7.4 t_sys_user(后台管理员) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | int(11) PK AUTO_INCREMENT | 管理员ID | +| user_name | varchar(125) | 姓名 | +| user_head | varchar(125) | 头像 | +| phone | varchar(11) | 手机号(登录账号) | +| password | varchar(64) | MD5 密码 | +| type | int(2) | 用户类型 (1=管理员) | +| status | int(2) | 状态 (1=正常) | + +### 7.5 t_sys_menu(菜单配置 — 三模块共享) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | varchar(32) PK | 菜单ID | +| pid | varchar(64) | 父级ID (0=一级) | +| menu_name | varchar(64) | 菜单名称 | +| url | varchar(255) | 菜单URL | +| seq_on | int(255) | 排序 | +| menu_icon | varchar(100) | 图标 | + +当前菜单数据: +``` +id=1 pid=0 menu_name=小程序 + ├── id=2 pid=1 menu_name=小程序设置 url=/admin/Wechatset/wechatRealTimeInfo + ├── id=3 pid=1 menu_name=小程序注册用户 url=/admin/Wechatinfro/wechatUserList + └── id=4 pid=1 menu_name=计算记录 url=/admin/Wechatinfro/wechatRecordList +``` + +> 注:三个模块共享此表。各模块的 `nav.js` 会在渲染时自动将 `/admin/` 替换为各自的模块路径。 + +### 7.6 t_sys_wechat_real_time_info(首页资讯 — 三模块共享) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | varchar(32) PK | 资讯ID | +| title_plain | varchar(255) | 标题 | +| thumbnail | varchar(125) | 缩略图 | +| excerpt_plain | text | 摘要 | +| url | varchar(125) | 详情链接 | +| create_time | datetime | 创建时间 | + +--- + +## 八、登录凭证 + +| 后台 | URL | 账号 | 密码 | +|------|-----|------|------| +| CPP | http://101.43.95.130/admincpp/login | 13212345678 | 123456 | +| GHD | http://101.43.95.130/adminghd/login | 13212345678 | 123456 | +| 通用 | http://101.43.95.130/admin/login | 13212345678 | 123456 | + +> 三个后台共用 `t_sys_user` 表,使用同一套账号密码。 + +--- + +## 九、服务器信息 + +| 项目 | 值 | +|------|-----| +| 服务器 IP | 101.43.95.130 | +| SSH 用户 | renjianbo / 123456 | +| Web 目录 | /www/wwwroot/code/ | +| PHP 版本 | PHP 7.x | +| 框架 | ThinkPHP 5.0 | +| 数据库 | MySQL (127.0.0.1), 库名 ruilai | +| 数据库用户 | rootrui / X2)jB+k%YH.p | +| Web 服务器 | Nginx | +| 管理面板 | 宝塔 (BT Panel) |