更新
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
# 医生角色关系说明
|
||||
|
||||
## 概述
|
||||
|
||||
医生和角色的关系通过中间表 `zyt_admin_role` 来管理。医生是具有 `role_id = 1` 角色的管理员。
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### 1. 管理员表 (zyt_admin)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `zyt_admin` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`name` varchar(50) NOT NULL COMMENT '管理员名称',
|
||||
`account` varchar(50) NOT NULL COMMENT '管理员账号',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
|
||||
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
|
||||
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
|
||||
`disable` tinyint(1) DEFAULT '0' COMMENT '是否禁用',
|
||||
`create_time` int(10) unsigned DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` int(10) unsigned DEFAULT NULL COMMENT '更新时间',
|
||||
`delete_time` int(10) unsigned DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员表';
|
||||
```
|
||||
|
||||
### 2. 角色表 (zyt_admin_role)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `zyt_admin_role` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`admin_id` int(11) NOT NULL COMMENT '管理员ID',
|
||||
`role_id` int(11) NOT NULL COMMENT '角色ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_admin_role` (`admin_id`, `role_id`),
|
||||
KEY `idx_admin_id` (`admin_id`),
|
||||
KEY `idx_role_id` (`role_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员角色中间表';
|
||||
```
|
||||
|
||||
### 3. 角色表 (zyt_admin_role_info)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `zyt_admin_role_info` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`name` varchar(50) NOT NULL COMMENT '角色名称',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
|
||||
`create_time` int(10) unsigned DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` int(10) unsigned DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色信息表';
|
||||
```
|
||||
|
||||
## 角色 ID 说明
|
||||
|
||||
| role_id | 角色名称 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 1 | 医生 | 医生角色 |
|
||||
| 2 | 管理员 | 系统管理员 |
|
||||
| ... | ... | 其他角色 |
|
||||
|
||||
## 查询医生的方式
|
||||
|
||||
### 方式1:通过中间表查询(推荐)
|
||||
|
||||
```php
|
||||
// 获取所有医生的 admin_id
|
||||
$adminIds = AdminRole::where('role_id', 1)->column('admin_id');
|
||||
|
||||
// 查询医生信息
|
||||
$doctors = Admin::whereIn('id', $adminIds)
|
||||
->where('disable', 0)
|
||||
->select();
|
||||
```
|
||||
|
||||
### 方式2:通过 Admin 模型的 role_id 属性
|
||||
|
||||
```php
|
||||
// Admin 模型中有 getRoleIdAttr 方法,会自动从中间表获取 role_id
|
||||
$admin = Admin::find(1);
|
||||
$roleIds = $admin->role_id; // 返回数组 [1, 2, ...]
|
||||
|
||||
// 检查是否是医生
|
||||
if (in_array(1, $roleIds)) {
|
||||
// 是医生
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现
|
||||
|
||||
### DoctorLogic.php
|
||||
|
||||
```php
|
||||
use app\common\model\auth\AdminRole;
|
||||
|
||||
// 获取医生列表
|
||||
$adminIds = AdminRole::where('role_id', 1)->column('admin_id');
|
||||
|
||||
$doctors = Admin::whereIn('id', $adminIds)
|
||||
->where('disable', 0)
|
||||
->select();
|
||||
|
||||
// 获取医生详情
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
|
||||
if ($hasRole) {
|
||||
$doctor = Admin::find($doctorId);
|
||||
}
|
||||
```
|
||||
|
||||
## API 端点
|
||||
|
||||
### 获取医生列表
|
||||
|
||||
**请求**:
|
||||
```
|
||||
GET /api/doctor/lists?page_no=1&page_size=10
|
||||
```
|
||||
|
||||
**流程**:
|
||||
1. 从 `zyt_admin_role` 表查询所有 `role_id = 1` 的 `admin_id`
|
||||
2. 从 `zyt_admin` 表查询这些 `admin_id` 的管理员信息
|
||||
3. 过滤 `disable = 0` 的管理员
|
||||
4. 返回医生列表
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"lists": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "张医生",
|
||||
"account": "doctor1",
|
||||
"avatar": "http://example.com/avatar.jpg",
|
||||
"mobile": "13800138000",
|
||||
"email": "doctor@example.com",
|
||||
"specialty": "医生",
|
||||
"title": "医生",
|
||||
"rating": "4.8"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"page_no": 1,
|
||||
"page_size": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 获取医生详情
|
||||
|
||||
**请求**:
|
||||
```
|
||||
GET /api/doctor/detail?id=2
|
||||
```
|
||||
|
||||
**流程**:
|
||||
1. 检查 `admin_id = 2` 是否在 `zyt_admin_role` 表中有 `role_id = 1` 的记录
|
||||
2. 如果有,从 `zyt_admin` 表查询医生信息
|
||||
3. 返回医生详情
|
||||
|
||||
### 获取医生排班
|
||||
|
||||
**请求**:
|
||||
```
|
||||
GET /api/doctor/roster?doctor_id=2&date=2024-03-11
|
||||
```
|
||||
|
||||
**流程**:
|
||||
1. 检查医生是否有医生角色
|
||||
2. 从 `la_doctor_roster` 表查询排班信息
|
||||
|
||||
## 添加医生的步骤
|
||||
|
||||
### 1. 创建管理员
|
||||
|
||||
在后台管理系统中创建新的管理员:
|
||||
- 名称:张医生
|
||||
- 账号:doctor1
|
||||
- 头像:上传头像
|
||||
- 手机:13800138000
|
||||
- 邮箱:doctor@example.com
|
||||
|
||||
### 2. 分配医生角色
|
||||
|
||||
在后台管理系统中为该管理员分配医生角色(role_id = 1)。
|
||||
|
||||
系统会自动在 `zyt_admin_role` 表中插入一条记录:
|
||||
```sql
|
||||
INSERT INTO zyt_admin_role (admin_id, role_id) VALUES (2, 1);
|
||||
```
|
||||
|
||||
### 3. 验证
|
||||
|
||||
调用 API 验证医生是否已添加:
|
||||
```
|
||||
GET /api/doctor/lists
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何查询某个管理员的所有角色?
|
||||
|
||||
A:
|
||||
```php
|
||||
$admin = Admin::find($adminId);
|
||||
$roleIds = $admin->role_id; // 返回数组
|
||||
```
|
||||
|
||||
### Q: 如何检查某个管理员是否是医生?
|
||||
|
||||
A:
|
||||
```php
|
||||
$hasRole = AdminRole::where('admin_id', $adminId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
```
|
||||
|
||||
### Q: 如何为管理员添加医生角色?
|
||||
|
||||
A:
|
||||
```php
|
||||
AdminRole::create([
|
||||
'admin_id' => $adminId,
|
||||
'role_id' => 1
|
||||
]);
|
||||
```
|
||||
|
||||
### Q: 如何移除管理员的医生角色?
|
||||
|
||||
A:
|
||||
```php
|
||||
AdminRole::where('admin_id', $adminId)
|
||||
->where('role_id', 1)
|
||||
->delete();
|
||||
```
|
||||
|
||||
### Q: 医生列表为空?
|
||||
|
||||
A: 检查以下几点:
|
||||
1. 是否有管理员被分配了医生角色(role_id = 1)
|
||||
2. 医生的 `disable` 字段是否为 0
|
||||
3. 医生是否被软删除(`delete_time` 不为空)
|
||||
|
||||
## 相关文件
|
||||
|
||||
- 医生逻辑: `server/app/api/logic/DoctorLogic.php`
|
||||
- 医生控制器: `server/app/api/controller/DoctorController.php`
|
||||
- Admin 模型: `server/app/common/model/auth/Admin.php`
|
||||
- AdminRole 模型: `server/app/common/model/auth/AdminRole.php`
|
||||
|
||||
## 总结
|
||||
|
||||
医生是通过中间表 `zyt_admin_role` 与管理员关联的。查询医生时需要:
|
||||
|
||||
1. 从 `zyt_admin_role` 表查询 `role_id = 1` 的所有 `admin_id`
|
||||
2. 从 `zyt_admin` 表查询这些 `admin_id` 的管理员信息
|
||||
3. 过滤 `disable = 0` 的管理员
|
||||
|
||||
这样可以确保只查询出具有医生角色的管理员。
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\order;
|
||||
|
||||
use app\common\model\Order;
|
||||
use think\response\Json;
|
||||
|
||||
/**
|
||||
* 支付宝支付回调控制器
|
||||
* Class AlipayNotifyController
|
||||
* @package app\adminapi\controller\order
|
||||
*/
|
||||
class AlipayNotifyController
|
||||
{
|
||||
/**
|
||||
* @notes 支付宝异步通知
|
||||
* @return string
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
try {
|
||||
// 获取支付宝配置
|
||||
$alipayConfig = config('pay.alipay');
|
||||
|
||||
// 获取POST数据
|
||||
$data = request()->post();
|
||||
|
||||
// 验证签名
|
||||
if (!$this->verifyAlipaySign($data, $alipayConfig['alipay_public_key'])) {
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
// 验证金额
|
||||
$order = Order::where('order_no', $data['out_trade_no'])->find();
|
||||
if (!$order) {
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
if ((float)$data['total_amount'] != (float)$order->amount) {
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
// 验证交易状态
|
||||
if ($data['trade_status'] == 'TRADE_SUCCESS' || $data['trade_status'] == 'TRADE_FINISHED') {
|
||||
// 更新订单状态
|
||||
if ($order->status == 1) { // 只有待支付的订单才能更新
|
||||
$order->status = 2; // 已支付
|
||||
$order->payment_method = 'alipay';
|
||||
$order->payment_time = date('Y-m-d H:i:s');
|
||||
$order->trade_no = $data['trade_no'];
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
return 'success';
|
||||
} catch (\Exception $e) {
|
||||
return 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 验证支付宝签名
|
||||
* @param array $data
|
||||
* @param string $publicKey
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyAlipaySign(array $data, string $publicKey): bool
|
||||
{
|
||||
try {
|
||||
// 获取签名
|
||||
$sign = $data['sign'] ?? '';
|
||||
unset($data['sign']);
|
||||
unset($data['sign_type']);
|
||||
|
||||
// 按键排序
|
||||
ksort($data);
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value !== '' && $value !== null) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
}
|
||||
$signStr = rtrim($signStr, '&');
|
||||
|
||||
// 验证签名
|
||||
$publicKeyResource = openssl_pkey_get_public($publicKey);
|
||||
$result = openssl_verify($signStr, base64_decode($sign), $publicKeyResource, OPENSSL_ALGO_SHA256);
|
||||
openssl_free_key($publicKeyResource);
|
||||
|
||||
return $result === 1;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class OrderController extends BaseAdminController
|
||||
public function detail()
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('detail');
|
||||
$detail = OrderLogic::detail($params['id']);
|
||||
$detail = OrderLogic::detail((int)$params['id']);
|
||||
|
||||
if (!$detail) {
|
||||
return $this->fail('订单不存在');
|
||||
@@ -66,7 +66,7 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('edit');
|
||||
|
||||
$result = OrderLogic::edit($params['id'], $params);
|
||||
$result = OrderLogic::edit((int)$params['id'], $params);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
@@ -82,7 +82,26 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('pay');
|
||||
|
||||
$result = OrderLogic::pay($params['id'], $params['payment_method']);
|
||||
// 判断是否是补单支付
|
||||
if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) {
|
||||
// 补单支付:通过订单号查找订单
|
||||
if (empty($params['order_no'])) {
|
||||
return $this->fail('补单支付需要提供订单号');
|
||||
}
|
||||
$order = \app\common\model\Order::where('order_no', $params['order_no'])->find();
|
||||
if (!$order) {
|
||||
return $this->fail('订单不存在');
|
||||
}
|
||||
$orderId = $order->id;
|
||||
} else {
|
||||
// 正常支付:通过订单ID
|
||||
if (empty($params['id'])) {
|
||||
return $this->fail('订单ID不能为空');
|
||||
}
|
||||
$orderId = (int)$params['id'];
|
||||
}
|
||||
|
||||
$result = OrderLogic::pay($orderId, $params['payment_method']);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
@@ -98,7 +117,7 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('cancel');
|
||||
|
||||
$result = OrderLogic::cancel($params['id']);
|
||||
$result = OrderLogic::cancel((int)$params['id']);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
@@ -114,7 +133,7 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('refund');
|
||||
|
||||
$result = OrderLogic::refund($params['id']);
|
||||
$result = OrderLogic::refund((int)$params['id']);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
@@ -130,7 +149,7 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('delete');
|
||||
|
||||
$result = OrderLogic::delete($params['id']);
|
||||
$result = OrderLogic::delete((int)$params['id']);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
@@ -146,4 +165,92 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
return $this->dataLists(new OrderLists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 支付宝支付
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function alipay()
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('pay');
|
||||
|
||||
// 补单支付不需要校验订单是否存在
|
||||
if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) {
|
||||
// 补单支付:直接返回支付URL,不校验订单
|
||||
$payUrl = OrderLogic::alipayPay(['order_no' => $params['order_no']]);
|
||||
if (!$payUrl) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
return $this->data(['pay_url' => $payUrl]);
|
||||
}
|
||||
|
||||
// 正常支付需要校验订单
|
||||
$order = $this->getOrderByParams($params);
|
||||
if (!$order) {
|
||||
return $this->fail('订单不存在');
|
||||
}
|
||||
|
||||
// 调用支付宝支付接口
|
||||
$payUrl = OrderLogic::alipayPay($order);
|
||||
if (!$payUrl) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->data(['pay_url' => $payUrl]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 微信支付
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function wechat()
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('pay');
|
||||
|
||||
// 补单支付不需要校验订单是否存在
|
||||
if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) {
|
||||
// 补单支付:直接返回支付数据,不校验订单
|
||||
$payData = OrderLogic::wechatPay(['order_no' => $params['order_no']]);
|
||||
if (!$payData) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
return $this->data($payData);
|
||||
}
|
||||
|
||||
// 正常支付需要校验订单
|
||||
$order = $this->getOrderByParams($params);
|
||||
if (!$order) {
|
||||
return $this->fail('订单不存在');
|
||||
}
|
||||
|
||||
// 调用微信支付接口
|
||||
$payData = OrderLogic::wechatPay($order);
|
||||
if (!$payData) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->data($payData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 根据参数获取订单
|
||||
* @param array $params
|
||||
* @return mixed
|
||||
*/
|
||||
private function getOrderByParams(array $params)
|
||||
{
|
||||
if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) {
|
||||
// 补单支付:通过订单号查找
|
||||
if (empty($params['order_no'])) {
|
||||
return null;
|
||||
}
|
||||
return \app\common\model\Order::where('order_no', $params['order_no'])->find();
|
||||
} else {
|
||||
// 正常支付:通过订单ID
|
||||
if (empty($params['id'])) {
|
||||
return null;
|
||||
}
|
||||
return \app\common\model\Order::find((int)$params['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\order;
|
||||
|
||||
use app\common\model\Order;
|
||||
use think\response\Xml;
|
||||
|
||||
/**
|
||||
* 微信支付回调控制器
|
||||
* Class WechatNotifyController
|
||||
* @package app\adminapi\controller\order
|
||||
*/
|
||||
class WechatNotifyController
|
||||
{
|
||||
/**
|
||||
* @notes 微信异步通知
|
||||
* @return Xml
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
try {
|
||||
// 获取微信配置
|
||||
$wechatConfig = config('pay.wechat');
|
||||
|
||||
// 获取XML数据
|
||||
$xmlData = file_get_contents('php://input');
|
||||
$data = $this->xmlToArray($xmlData);
|
||||
|
||||
// 验证签名
|
||||
if (!$this->verifyWechatSign($data, $wechatConfig['api_key'])) {
|
||||
return $this->xmlResponse('FAIL', '签名验证失败');
|
||||
}
|
||||
|
||||
// 验证金额
|
||||
$order = Order::where('order_no', $data['out_trade_no'])->find();
|
||||
if (!$order) {
|
||||
return $this->xmlResponse('FAIL', '订单不存在');
|
||||
}
|
||||
|
||||
if ((int)$data['total_fee'] != (int)($order->amount * 100)) {
|
||||
return $this->xmlResponse('FAIL', '金额不匹配');
|
||||
}
|
||||
|
||||
// 验证交易状态
|
||||
if ($data['result_code'] == 'SUCCESS') {
|
||||
// 更新订单状态
|
||||
if ($order->status == 1) { // 只有待支付的订单才能更新
|
||||
$order->status = 2; // 已支付
|
||||
$order->payment_method = 'wechat';
|
||||
$order->payment_time = date('Y-m-d H:i:s');
|
||||
$order->trade_no = $data['transaction_id'];
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->xmlResponse('SUCCESS', '支付成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->xmlResponse('FAIL', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 验证微信签名
|
||||
* @param array $data
|
||||
* @param string $apiKey
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyWechatSign(array $data, string $apiKey): bool
|
||||
{
|
||||
try {
|
||||
// 获取签名
|
||||
$sign = $data['sign'] ?? '';
|
||||
unset($data['sign']);
|
||||
|
||||
// 按键排序
|
||||
ksort($data);
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value !== '' && $value !== null) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
}
|
||||
$signStr .= 'key=' . $apiKey;
|
||||
|
||||
// MD5签名
|
||||
$computedSign = strtoupper(md5($signStr));
|
||||
|
||||
return $computedSign === $sign;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes XML转数组
|
||||
* @param string $xml
|
||||
* @return array
|
||||
*/
|
||||
private function xmlToArray(string $xml): array
|
||||
{
|
||||
try {
|
||||
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||||
return is_array($data) ? $data : [];
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 返回XML响应
|
||||
* @param string $returnCode
|
||||
* @param string $returnMsg
|
||||
* @return Xml
|
||||
*/
|
||||
private function xmlResponse(string $returnCode, string $returnMsg): Xml
|
||||
{
|
||||
$xml = '<xml>';
|
||||
$xml .= '<return_code><![CDATA[' . $returnCode . ']]></return_code>';
|
||||
$xml .= '<return_msg><![CDATA[' . $returnMsg . ']]></return_msg>';
|
||||
$xml .= '</xml>';
|
||||
|
||||
return response($xml, 200, ['Content-Type' => 'application/xml']);
|
||||
}
|
||||
}
|
||||
@@ -233,7 +233,24 @@ class DiagnosisController extends BaseAdminController
|
||||
$result = DiagnosisLogic::getCallRecords($params);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者通话签名(用于测试)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getDoctorSignature()
|
||||
{
|
||||
$params = $this->request->get();
|
||||
|
||||
if (empty($params['patient_id'])) {
|
||||
return $this->fail('医助理ID不能为空');
|
||||
}
|
||||
|
||||
$result = DiagnosisLogic::getDoctorSignature((int)$params['patient_id']);
|
||||
if ($result) {
|
||||
return $this->data($result);
|
||||
}
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
/**
|
||||
* @notes 获取患者通话签名(用于测试)
|
||||
* @return \think\response\Json
|
||||
@@ -300,14 +317,14 @@ class DiagnosisController extends BaseAdminController
|
||||
|
||||
$offset = ($page_no - 1) * $page_size;
|
||||
|
||||
$lists = \app\common\model\tcm\Diagnosis::where('patient_name|patient_phone|patient_id_card', 'like', '%' . $keyword . '%')
|
||||
->field(['id', 'patient_name', 'patient_phone', 'patient_id_card', 'gender', 'age'])
|
||||
$lists = \app\common\model\tcm\Diagnosis::where('patient_name|phone|id_card', 'like', '%' . $keyword . '%')
|
||||
->field(['id', 'patient_name', 'phone', 'id_card', 'gender', 'age'])
|
||||
->limit($offset, $page_size)
|
||||
->order('id desc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$count = \app\common\model\tcm\Diagnosis::where('patient_name|patient_phone|patient_id_card', 'like', '%' . $keyword . '%')
|
||||
$count = \app\common\model\tcm\Diagnosis::where('patient_name|phone|id_card', 'like', '%' . $keyword . '%')
|
||||
->count();
|
||||
|
||||
return $this->success('', [
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace app\adminapi\lists\order;
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
use app\common\model\Order;
|
||||
use app\common\model\auth\AdminRole;
|
||||
|
||||
/**
|
||||
* 订单列表
|
||||
@@ -27,6 +28,33 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 权限过滤 - 普通员工只能看自己创建的订单
|
||||
* @param array $where
|
||||
* @return void
|
||||
*/
|
||||
private function filterByPermission(array &$where): void
|
||||
{
|
||||
// 检查是否是超管(root字段为1)
|
||||
if (!empty($this->adminInfo['root']) && $this->adminInfo['root'] == 1) {
|
||||
// 超管不过滤,可以看所有订单
|
||||
return;
|
||||
}
|
||||
|
||||
// 主管角色ID列表(可根据实际调整)
|
||||
$supervisorRoles = [0, 3];
|
||||
|
||||
// 检查当前用户是否是主管
|
||||
$roleIds = \app\common\model\auth\AdminRole::where('admin_id', $this->adminId)->column('role_id');
|
||||
|
||||
// 如果不是主管,只能看自己创建的订单
|
||||
$isSupervisor = count(array_intersect($roleIds, $supervisorRoles)) > 0;
|
||||
|
||||
if (!$isSupervisor) {
|
||||
$where[] = ['creator_id', '=', $this->adminId];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取列表
|
||||
* @return array
|
||||
@@ -38,11 +66,14 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理患者关键词搜索
|
||||
// 权限控制:普通员工只能看自己创建的订单
|
||||
$this->filterByPermission($where);
|
||||
|
||||
// 处理患者关键词搜索(从诊单表搜索)
|
||||
if (!empty($this->params['patient_keyword'])) {
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('user')
|
||||
->where('nickname|mobile', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->where('patient_name|patient_phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
@@ -57,7 +88,7 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
}
|
||||
|
||||
return Order::where($where)
|
||||
->with(['patient', 'creator', 'details'])
|
||||
->with(['patient', 'creator'])
|
||||
->order(['create_time' => 'desc'])
|
||||
->limit($this->limitOffset, $this->pageSize)
|
||||
->select()
|
||||
@@ -72,11 +103,14 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
$where = $this->searchWhere;
|
||||
|
||||
// 处理患者关键词搜索
|
||||
// 权限控制:普通员工只能看自己创建的订单
|
||||
$this->filterByPermission($where);
|
||||
|
||||
// 处理患者关键词搜索(从诊单表搜索)
|
||||
if (!empty($this->params['patient_keyword'])) {
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('user')
|
||||
->where('nickname|mobile', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->where('patient_name|patient_phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@ class OrderLogic
|
||||
public static function create(array $params)
|
||||
{
|
||||
try {
|
||||
Db::startTrans();
|
||||
|
||||
$order = new Order();
|
||||
$order->order_no = self::generateOrderNo();
|
||||
$order->patient_id = $params['patient_id'];
|
||||
@@ -44,24 +42,8 @@ class OrderLogic
|
||||
$order->remark = $params['remark'] ?? '';
|
||||
$order->save();
|
||||
|
||||
// 创建订单详情
|
||||
if (!empty($params['details'])) {
|
||||
foreach ($params['details'] as $detail) {
|
||||
$orderDetail = new OrderDetail();
|
||||
$orderDetail->order_id = $order->id;
|
||||
$orderDetail->related_type = $detail['related_type'];
|
||||
$orderDetail->related_id = $detail['related_id'];
|
||||
$orderDetail->quantity = $detail['quantity'] ?? 1;
|
||||
$orderDetail->unit_price = $detail['unit_price'];
|
||||
$orderDetail->total_price = $detail['total_price'];
|
||||
$orderDetail->save();
|
||||
}
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
return $order;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
@@ -198,9 +180,6 @@ class OrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
// 删除订单详情
|
||||
OrderDetail::where('order_id', $id)->delete();
|
||||
|
||||
// 删除订单
|
||||
$order->delete();
|
||||
|
||||
@@ -218,7 +197,7 @@ class OrderLogic
|
||||
*/
|
||||
public static function detail(int $id): ?array
|
||||
{
|
||||
$order = Order::with(['patient', 'creator', 'details'])
|
||||
$order = Order::with(['patient', 'creator'])
|
||||
->find($id);
|
||||
|
||||
if (!$order) {
|
||||
@@ -239,4 +218,213 @@ class OrderLogic
|
||||
{
|
||||
return self::$error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成支付宝签名
|
||||
* @param array $params
|
||||
* @param string $privateKey
|
||||
* @return string
|
||||
*/
|
||||
private static function generateAlipaySign(array $params, string $privateKey): string
|
||||
{
|
||||
// 移除sign字段
|
||||
unset($params['sign']);
|
||||
|
||||
// 按键排序
|
||||
ksort($params);
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($params as $key => $value) {
|
||||
if ($value !== '' && $value !== null) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
}
|
||||
$signStr = rtrim($signStr, '&');
|
||||
|
||||
// 使用私钥签名
|
||||
$privateKeyResource = openssl_pkey_get_private($privateKey);
|
||||
openssl_sign($signStr, $sign, $privateKeyResource, OPENSSL_ALGO_SHA256);
|
||||
openssl_free_key($privateKeyResource);
|
||||
|
||||
return base64_encode($sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成微信签名
|
||||
* @param array $params
|
||||
* @param string $apiKey
|
||||
* @return string
|
||||
*/
|
||||
private static function generateWechatSign(array $params, string $apiKey): string
|
||||
{
|
||||
// 移除sign字段
|
||||
unset($params['sign']);
|
||||
|
||||
// 按键排序
|
||||
ksort($params);
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($params as $key => $value) {
|
||||
if ($value !== '' && $value !== null) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
}
|
||||
$signStr .= 'key=' . $apiKey;
|
||||
|
||||
// MD5签名
|
||||
return strtoupper(md5($signStr));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成随机字符串
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
private static function generateNonceStr(int $length = 32): string
|
||||
{
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
$str = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 支付宝支付
|
||||
* @param $order
|
||||
* @return string|false
|
||||
*/
|
||||
public static function alipayPay($order)
|
||||
{
|
||||
try {
|
||||
// 这里调用支付宝SDK生成支付链接
|
||||
// 示例:生成支付宝支付URL
|
||||
$payUrl = self::generateAlipayUrl($order);
|
||||
return $payUrl;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 微信支付
|
||||
* @param $order
|
||||
* @return array|false
|
||||
*/
|
||||
public static function wechatPay($order)
|
||||
{
|
||||
try {
|
||||
// 这里调用微信SDK生成支付二维码或链接
|
||||
// 示例:生成微信支付数据
|
||||
$payData = self::generateWechatPayData($order);
|
||||
return $payData;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成支付宝支付URL
|
||||
* @param $order
|
||||
* @return string
|
||||
*/
|
||||
private static function generateAlipayUrl($order): string
|
||||
{
|
||||
try {
|
||||
// 获取订单号
|
||||
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
|
||||
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
|
||||
|
||||
// 获取支付宝配置
|
||||
$alipayConfig = config('pay.alipay');
|
||||
|
||||
if (empty($alipayConfig['app_id']) || empty($alipayConfig['merchant_private_key'])) {
|
||||
throw new \Exception('支付宝配置不完整');
|
||||
}
|
||||
|
||||
// 使用支付宝SDK生成支付URL
|
||||
$params = [
|
||||
'app_id' => $alipayConfig['app_id'],
|
||||
'method' => 'alipay.trade.page.pay',
|
||||
'charset' => 'UTF-8',
|
||||
'sign_type' => 'RSA2',
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'version' => '1.0',
|
||||
'notify_url' => $alipayConfig['notify_url'] ?: config('app.url') . '/api/order/alipay-notify',
|
||||
'return_url' => $alipayConfig['return_url'] ?: config('app.url') . '/order',
|
||||
'biz_content' => json_encode([
|
||||
'out_trade_no' => $orderNo,
|
||||
'product_code' => 'FAST_INSTANT_TRADE_PAY',
|
||||
'total_amount' => $amount,
|
||||
'subject' => '订单支付',
|
||||
'body' => '订单号:' . $orderNo,
|
||||
], JSON_UNESCAPED_UNICODE),
|
||||
];
|
||||
|
||||
// 生成签名
|
||||
$sign = self::generateAlipaySign($params, $alipayConfig['merchant_private_key']);
|
||||
$params['sign'] = $sign;
|
||||
|
||||
// 生成支付URL
|
||||
$payUrl = $alipayConfig['gateway_url'] . '?' . http_build_query($params);
|
||||
|
||||
return $payUrl;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 生成微信支付数据
|
||||
* @param $order
|
||||
* @return array
|
||||
*/
|
||||
private static function generateWechatPayData($order): array
|
||||
{
|
||||
try {
|
||||
// 获取订单号
|
||||
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
|
||||
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
|
||||
|
||||
// 获取微信配置
|
||||
$wechatConfig = config('pay.wechat');
|
||||
|
||||
if (empty($wechatConfig['app_id']) || empty($wechatConfig['mch_id']) || empty($wechatConfig['api_key'])) {
|
||||
throw new \Exception('微信支付配置不完整');
|
||||
}
|
||||
|
||||
// 生成微信支付数据
|
||||
$payData = [
|
||||
'appid' => $wechatConfig['app_id'],
|
||||
'mch_id' => $wechatConfig['mch_id'],
|
||||
'nonce_str' => self::generateNonceStr(),
|
||||
'body' => '订单支付',
|
||||
'out_trade_no' => $orderNo,
|
||||
'total_fee' => (int)($amount * 100), // 转换为分
|
||||
'spbill_create_ip' => request()->ip() ?: '127.0.0.1',
|
||||
'notify_url' => $wechatConfig['notify_url'] ?: config('app.url') . '/api/order/wechat-notify',
|
||||
'trade_type' => 'NATIVE', // 二维码支付
|
||||
];
|
||||
|
||||
// 生成签名
|
||||
$sign = self::generateWechatSign($payData, $wechatConfig['api_key']);
|
||||
$payData['sign'] = $sign;
|
||||
|
||||
// 返回支付数据
|
||||
return [
|
||||
'pay_url' => $wechatConfig['gateway_url'] . '/pay/unifiedorder',
|
||||
'pay_data' => $payData,
|
||||
'order_no' => $orderNo,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,6 +703,55 @@ class DiagnosisLogic extends BaseLogic
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @notes 获取医助签名(供小程序调用)
|
||||
* @param int $patientId
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function getDoctorSignature(int $patientId)
|
||||
{
|
||||
|
||||
try {
|
||||
// 获取配置
|
||||
$config = self::getTrtcConfig();
|
||||
|
||||
if (!$config) {
|
||||
self::setError('请先配置腾讯云TRTC参数');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 患者userId
|
||||
$patientUserId = 'doctor_' . $patientId;
|
||||
|
||||
// 生成患者的 UserSig
|
||||
$userSig = self::generateUserSig(
|
||||
$config['sdkAppId'],
|
||||
$config['secretKey'],
|
||||
$patientUserId
|
||||
);
|
||||
|
||||
if (!$userSig) {
|
||||
self::setError('生成签名失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 确保患者账号已导入IM
|
||||
self::ensurePatientImAccount($patientId, $patientUserId);
|
||||
|
||||
\think\facade\Log::info('小程序获取患者签名成功 - doctor_id: ' . $patientId . ', user_id: ' . $patientUserId);
|
||||
|
||||
return [
|
||||
'sdkAppId' => (int)$config['sdkAppId'],
|
||||
'userId' => $patientUserId,
|
||||
'userSig' => $userSig,
|
||||
'expireTime' => 86400
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error('获取患者签名失败 - patient_id: ' . $patientId . ', error: ' . $e->getMessage());
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取患者签名(供小程序调用)
|
||||
|
||||
@@ -16,17 +16,17 @@ class OrderValidate extends BaseValidate
|
||||
protected $rule = [
|
||||
'id' => 'require|integer',
|
||||
'patient_id' => 'require|integer',
|
||||
'creator_id' => 'require|integer',
|
||||
'order_type' => 'require|in:1,2,3',
|
||||
'amount' => 'require|float',
|
||||
'status' => 'require|in:1,2,3,4',
|
||||
'payment_method' => 'in:alipay,wechat,bank',
|
||||
'payment_method' => 'in:alipay,wechat',
|
||||
'remark' => 'string|max:500',
|
||||
'order_no' => 'string|max:50',
|
||||
'is_supplement' => 'in:0,1',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
'patient_id.require' => '患者ID必填',
|
||||
'creator_id.require' => '创建人ID必填',
|
||||
'order_type.require' => '订单类型必填',
|
||||
'order_type.in' => '订单类型不正确',
|
||||
'amount.require' => '订单金额必填',
|
||||
@@ -36,10 +36,10 @@ class OrderValidate extends BaseValidate
|
||||
];
|
||||
|
||||
protected $scene = [
|
||||
'create' => ['patient_id', 'creator_id', 'order_type', 'amount'],
|
||||
'create' => ['patient_id', 'order_type', 'amount'],
|
||||
'edit' => ['id', 'remark'],
|
||||
'detail' => ['id'],
|
||||
'pay' => ['id', 'payment_method'],
|
||||
'pay' => ['payment_method'],
|
||||
'cancel' => ['id'],
|
||||
'refund' => ['id'],
|
||||
'delete' => ['id'],
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\api\logic\DoctorLogic;
|
||||
|
||||
/**
|
||||
* 医生控制器
|
||||
* Class DoctorController
|
||||
* @package app\api\controller
|
||||
*/
|
||||
class DoctorController extends BaseApiController
|
||||
{
|
||||
public array $notNeedLogin = ['lists', 'detail'];
|
||||
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
$page_no = $this->request->get('page_no', 1);
|
||||
$page_size = $this->request->get('page_size', 10);
|
||||
$keyword = $this->request->get('keyword', '');
|
||||
|
||||
$params = [
|
||||
'page_no' => $page_no,
|
||||
'page_size' => $page_size,
|
||||
'keyword' => $keyword
|
||||
];
|
||||
|
||||
$result = DoctorLogic::getDoctorList($params);
|
||||
return $this->success('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生详情
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$doctorId = $this->request->get('id', 0);
|
||||
|
||||
if (!$doctorId) {
|
||||
return $this->fail('医生ID不能为空');
|
||||
}
|
||||
|
||||
$result = DoctorLogic::getDoctorDetail($doctorId);
|
||||
|
||||
if (!$result) {
|
||||
return $this->fail('医生不存在');
|
||||
}
|
||||
|
||||
return $this->success('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生排班
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function roster()
|
||||
{
|
||||
$doctorId = $this->request->get('doctor_id', 0);
|
||||
$date = $this->request->get('date', '');
|
||||
|
||||
if (!$doctorId || !$date) {
|
||||
return $this->fail('参数不能为空');
|
||||
}
|
||||
|
||||
$result = DoctorLogic::getDoctorRoster($doctorId, $date);
|
||||
return $this->success('', $result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\model\doctor\Roster;
|
||||
|
||||
/**
|
||||
* 医生逻辑层
|
||||
* Class DoctorLogic
|
||||
* @package app\api\logic
|
||||
*/
|
||||
class DoctorLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public static function getDoctorList($params = [])
|
||||
{
|
||||
$page_no = $params['page_no'] ?? 1;
|
||||
$page_size = $params['page_size'] ?? 10;
|
||||
$keyword = $params['keyword'] ?? '';
|
||||
|
||||
$offset = ($page_no - 1) * $page_size;
|
||||
|
||||
// 通过中间表 zyt_admin_role 查询 role_id = 1 的医生
|
||||
$adminIds = AdminRole::where('role_id', 1)->column('admin_id');
|
||||
|
||||
if (empty($adminIds)) {
|
||||
return [
|
||||
'lists' => [],
|
||||
'count' => 0,
|
||||
'page_no' => $page_no,
|
||||
'page_size' => $page_size
|
||||
];
|
||||
}
|
||||
|
||||
$query = Admin::whereIn('id', $adminIds)
|
||||
->where('disable', 0);
|
||||
|
||||
// 搜索关键词
|
||||
if (!empty($keyword)) {
|
||||
$query->where('name|account', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$lists = $query->field(['id', 'name', 'account', 'avatar'])
|
||||
->limit($offset, $page_size)
|
||||
->order('id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 处理医生数据
|
||||
$doctors = [];
|
||||
foreach ($lists as $doctor) {
|
||||
$doctors[] = [
|
||||
'id' => $doctor['id'],
|
||||
'name' => $doctor['name'],
|
||||
'account' => $doctor['account'],
|
||||
'avatar' => $doctor['avatar'] ?? '',
|
||||
'specialty' => '医生', // 可根据需要扩展
|
||||
'title' => '医生', // 可根据需要扩展
|
||||
'rating' => '9.8', // 可根据需要从其他表获取
|
||||
];
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
$countQuery = Admin::whereIn('id', $adminIds)
|
||||
->where('disable', 0);
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$countQuery->where('name|account', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$count = $countQuery->count();
|
||||
|
||||
return [
|
||||
'lists' => $doctors,
|
||||
'count' => $count,
|
||||
'page_no' => $page_no,
|
||||
'page_size' => $page_size
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生详情
|
||||
* @param int $doctorId
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getDoctorDetail($doctorId)
|
||||
{
|
||||
// 检查该医生是否有医生角色(role_id = 1)
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
|
||||
if (!$hasRole) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$doctor = Admin::where('id', $doctorId)
|
||||
->where('disable', 0)
|
||||
->field(['id', 'name', 'account', 'avatar', 'mobile', 'email'])
|
||||
->find();
|
||||
|
||||
if (!$doctor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$doctorData = $doctor->toArray();
|
||||
|
||||
return [
|
||||
'id' => $doctorData['id'],
|
||||
'name' => $doctorData['name'],
|
||||
'account' => $doctorData['account'],
|
||||
'avatar' => $doctorData['avatar'] ?? '',
|
||||
'mobile' => $doctorData['mobile'] ?? '',
|
||||
'email' => $doctorData['email'] ?? '',
|
||||
'specialty' => '医生',
|
||||
'title' => '医生',
|
||||
'rating' => '4.8',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生排班
|
||||
* @param int $doctorId
|
||||
* @param string $date
|
||||
* @return array
|
||||
*/
|
||||
public static function getDoctorRoster($doctorId, $date)
|
||||
{
|
||||
// 检查该医生是否有医生角色
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
|
||||
if (!$hasRole) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rosters = Roster::where('doctor_id', $doctorId)
|
||||
->where('date', $date)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
return $rosters;
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,12 @@ class Order extends BaseModel
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 关联患者
|
||||
* @notes 关联患者(从诊单表)
|
||||
* @return \think\model\relation\BelongsTo
|
||||
*/
|
||||
public function patient()
|
||||
{
|
||||
return $this->belongsTo('app\common\model\user\User', 'patient_id', 'id');
|
||||
return $this->belongsTo('app\common\model\tcm\Diagnosis', 'patient_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +42,7 @@ class Order extends BaseModel
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo('app\common\model\admin\Admin', 'creator_id', 'id');
|
||||
return $this->belongsTo('app\common\model\auth\Admin', 'creator_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +62,7 @@ class Order extends BaseModel
|
||||
{
|
||||
if ($value) {
|
||||
$query->whereHas('patient', function ($q) use ($value) {
|
||||
$q->where('nickname|mobile', 'like', '%' . $value . '%');
|
||||
$q->where('patient_name|patient_phone', 'like', '%' . $value . '%');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -133,8 +133,12 @@ class Order extends BaseModel
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
$patient = $this->patient()->find();
|
||||
return $patient ? $patient->nickname : '';
|
||||
try {
|
||||
$patient = $this->patient()->find();
|
||||
return $patient ? $patient->patient_name : '';
|
||||
} catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,8 +149,12 @@ class Order extends BaseModel
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
$patient = $this->patient()->find();
|
||||
return $patient ? $patient->mobile : '';
|
||||
try {
|
||||
$patient = $this->patient()->find();
|
||||
return $patient ? $patient->patient_phone : '';
|
||||
} catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +165,11 @@ class Order extends BaseModel
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
$creator = $this->creator()->find();
|
||||
return $creator ? $creator->name : '';
|
||||
try {
|
||||
$creator = $this->creator()->find();
|
||||
return $creator ? $creator->name : '';
|
||||
} catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 支付配置
|
||||
*/
|
||||
return [
|
||||
// 支付宝配置
|
||||
'alipay' => [
|
||||
'app_id' => env('ALIPAY_APP_ID', ''),
|
||||
'merchant_private_key' => env('ALIPAY_MERCHANT_PRIVATE_KEY', ''),
|
||||
'alipay_public_key' => env('ALIPAY_PUBLIC_KEY', ''),
|
||||
'notify_url' => env('ALIPAY_NOTIFY_URL', ''),
|
||||
'return_url' => env('ALIPAY_RETURN_URL', ''),
|
||||
'gateway_url' => env('ALIPAY_GATEWAY_URL', 'https://openapi.alipay.com/gateway.do'),
|
||||
],
|
||||
|
||||
// 微信支付配置
|
||||
'wechat' => [
|
||||
'app_id' => env('WECHAT_APP_ID', ''),
|
||||
'mch_id' => env('WECHAT_MCH_ID', ''),
|
||||
'api_key' => env('WECHAT_API_KEY', ''),
|
||||
'notify_url' => env('WECHAT_NOTIFY_URL', ''),
|
||||
'gateway_url' => env('WECHAT_GATEWAY_URL', 'https://api.mch.weixin.qq.com'),
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,46 @@
|
||||
-- 添加测试医生数据
|
||||
|
||||
-- 1. 添加医生管理员到 zyt_admin 表
|
||||
INSERT INTO `zyt_admin` (`name`, `account`, `avatar`, `mobile`, `email`, `disable`, `create_time`, `update_time`) VALUES
|
||||
('张医生', 'doctor1', '/uploads/avatar/doctor1.jpg', '13800138000', 'doctor1@example.com', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
('李医生', 'doctor2', '/uploads/avatar/doctor2.jpg', '13800138001', 'doctor2@example.com', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
('王医生', 'doctor3', '/uploads/avatar/doctor3.jpg', '13800138002', 'doctor3@example.com', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
('陈医生', 'doctor4', '/uploads/avatar/doctor4.jpg', '13800138003', 'doctor4@example.com', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||||
|
||||
-- 2. 为医生分配医生角色(role_id = 1)
|
||||
-- 假设上面插入的医生 ID 分别为 2, 3, 4, 5
|
||||
INSERT INTO `zyt_admin_role` (`admin_id`, `role_id`) VALUES
|
||||
(2, 1),
|
||||
(3, 1),
|
||||
(4, 1),
|
||||
(5, 1);
|
||||
|
||||
-- 3. 验证医生数据
|
||||
-- 查询所有医生
|
||||
SELECT a.id, a.name, a.account, a.avatar, a.mobile, a.email
|
||||
FROM `zyt_admin` a
|
||||
INNER JOIN `zyt_admin_role` ar ON a.id = ar.admin_id
|
||||
WHERE ar.role_id = 1 AND a.disable = 0;
|
||||
|
||||
-- 4. 添加医生排班数据(可选)
|
||||
-- 假设医生 ID 为 2, 3, 4, 5
|
||||
INSERT INTO `la_doctor_roster` (`doctor_id`, `date`, `period`, `status`, `quota`, `max_patients`, `create_time`, `update_time`) VALUES
|
||||
-- 医生 2 的排班
|
||||
(2, '2024-03-11', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(2, '2024-03-11', 'afternoon', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(2, '2024-03-12', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
|
||||
-- 医生 3 的排班
|
||||
(3, '2024-03-11', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(3, '2024-03-12', 'afternoon', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
|
||||
-- 医生 4 的排班
|
||||
(4, '2024-03-11', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(4, '2024-03-13', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
|
||||
-- 医生 5 的排班
|
||||
(5, '2024-03-11', 'afternoon', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||||
(5, '2024-03-12', 'morning', 1, 20, 30, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||||
|
||||
-- 5. 验证排班数据
|
||||
SELECT * FROM `la_doctor_roster` WHERE doctor_id IN (2, 3, 4, 5);
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* 医生API测试脚本
|
||||
* 用于测试医生列表、详情和排班API
|
||||
*/
|
||||
|
||||
// 设置基础URL
|
||||
$baseUrl = 'http://localhost:8000/api';
|
||||
|
||||
// 测试函数
|
||||
function testApi($method, $endpoint, $params = []) {
|
||||
global $baseUrl;
|
||||
|
||||
$url = $baseUrl . $endpoint;
|
||||
|
||||
// 构建查询字符串
|
||||
if ($method === 'GET' && !empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
echo "\n========================================\n";
|
||||
echo "测试: $method $endpoint\n";
|
||||
echo "URL: $url\n";
|
||||
echo "参数: " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
echo "========================================\n";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
if ($method === 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
echo "错误: $error\n";
|
||||
return null;
|
||||
}
|
||||
|
||||
echo "HTTP状态码: $httpCode\n";
|
||||
|
||||
$data = json_decode($response, true);
|
||||
echo "响应: " . json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
echo "开始测试医生API...\n";
|
||||
|
||||
// 1. 测试获取医生列表
|
||||
echo "\n\n【测试1】获取医生列表\n";
|
||||
$result1 = testApi('GET', '/doctor/lists', [
|
||||
'page_no' => 1,
|
||||
'page_size' => 10
|
||||
]);
|
||||
|
||||
// 2. 测试获取医生列表(带搜索)
|
||||
echo "\n\n【测试2】获取医生列表(带搜索)\n";
|
||||
$result2 = testApi('GET', '/doctor/lists', [
|
||||
'page_no' => 1,
|
||||
'page_size' => 10,
|
||||
'keyword' => '医生'
|
||||
]);
|
||||
|
||||
// 3. 测试获取医生详情
|
||||
if ($result1 && isset($result1['data']['lists'][0])) {
|
||||
$doctorId = $result1['data']['lists'][0]['id'];
|
||||
echo "\n\n【测试3】获取医生详情 (ID: $doctorId)\n";
|
||||
$result3 = testApi('GET', '/doctor/detail', [
|
||||
'id' => $doctorId
|
||||
]);
|
||||
|
||||
// 4. 测试获取医生排班
|
||||
echo "\n\n【测试4】获取医生排班 (ID: $doctorId)\n";
|
||||
$result4 = testApi('GET', '/doctor/roster', [
|
||||
'doctor_id' => $doctorId,
|
||||
'date' => date('Y-m-d')
|
||||
]);
|
||||
}
|
||||
|
||||
echo "\n\n测试完成!\n";
|
||||
?>
|
||||
Reference in New Issue
Block a user