1
0
Fork 0
master
panliang 2023-10-15 13:39:09 +08:00
parent 647c0807d0
commit c8d9667d2e
47 changed files with 501 additions and 10 deletions

View File

@ -49,9 +49,8 @@ class AuthController extends AdminAuthController
$openid = $request->input('openid');
$open_type = $request->input('open_type');
if ($openid && $open_type) {
UserSocialite::where(['openid' => $openid, 'type' => SocialiteType::from($open_type)])->update([
UserSocialite::where(['openid' => $openid, 'type' => SocialiteType::from($open_type), 'user_type' => $user->getMorphClass()])->update([
'user_id' => $user->id,
'user_type' => $user->getMorphClass(),
]);
}

View File

@ -31,6 +31,7 @@ class UserController extends AdminController
]))
->columns([
amisMake()->TableColumn()->name('id')->label(__('user.id')),
amisMake()->TableColumn()->type('avatar')->src('${avatar}')->name('avatar')->label(__('user.avatar')),
amisMake()->TableColumn()->name('phone')->label(__('user.phone')),
amisMake()->TableColumn()->name('name')->label(__('user.name')),
amisMake()->TableColumn()->name('sex_text')->label(__('user.sex')),
@ -49,6 +50,7 @@ class UserController extends AdminController
amisMake()->TextControl()->name('phone')->label(__('user.phone'))->required(),
amisMake()->TextControl()->set('type', 'input-password')->name('password')->label(__('user.password'))->required(!$isEdit),
amisMake()->TextControl()->name('name')->label(__('user.name')),
amisMake()->ImageControl()->name('avatar')->label(__('user.avatar')),
amisMake()->SelectControl()->options(Gender::options())->name('sex')->label(__('user.sex')),
amisMake()->TextControl()->name('address')->label(__('user.address')),
amisMake()->DateControl()->name('birthday')->label(__('user.birthday')),

View File

@ -61,6 +61,7 @@ class PatientRecordService extends BaseService
$data['creator_id'] = Admin::user()->id;
$patient = Patient::findOrFail(data_get($data, 'patient_id'));
$type = $patient->type;
$data['user_id'] = $patient->user_id;
$data['type_id'] = $patient->type_id;
$data['saler_id'] = $patient->saler_id;
$data['inviter_id'] = $patient->inviter_id;

View File

@ -3,6 +3,8 @@
namespace App\Admin\Services;
use App\ModelFilters\UserFilter;
use App\Models\Patient;
use App\Models\PatientRecord;
use App\Models\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
@ -38,7 +40,7 @@ class UserService extends BaseService
$phoneRule->ignore($model->id);
}
$validator = Validator::make($data, [
'phone' => ['required', 'phone', $phoneRule],
'phone' => [Rule::requiredIf(!$model), 'phone', $phoneRule],
], [
'phone.required' => __('user.phone') . '必填',
'phone.phone' => __('user.phone') . ' 不是合法手机号',
@ -50,4 +52,13 @@ class UserService extends BaseService
return true;
}
public function preDelete(array $ids)
{
// 删除病人记录
Patient::whereIn('user_id', $ids)->delete();
// 删除病历记录
PatientRecord::whereIn('user_id', $ids)->delete();
return true;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class UserBirthdayNotify extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:birthday-notify';
/**
* The console command description.
*
* @var string
*/
protected $description = '用户生日通知';
/**
* Execute the console command.
*/
public function handle()
{
$now = now();
$users = User::whereDay('birthday', $now->day)->whereMonth('birthday', $now->month)->get();
$phone = $users->implode('phone', ',');
try {
$easySms = new EasySms(config('easysms'));
$result = $easySms->send($phone, [
'template' => 'SMS_463637721',
]);
logger('生日短信发送成功: ' . $phone, $result);
} catch (\Exception $e) {
logger()->error('生日短信发送失败', $e->getException('aliyun')->raw);
}
}
}

View File

@ -15,6 +15,7 @@ class Kernel extends ConsoleKernel
// $schedule->command('inspire')->hourly();
// $schedule->call(fn () => logger('schedule running'))->everyMinute();
$schedule->command('patient-record:notify')->daily();
$schedule->command('user:birthday-notify')->dailyAt('8:00');
}
/**

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers\Client;
use App\Admin\Services\UserService;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AccountController extends Controller
{
public function profile(Request $request)
{
$user = auth('api')->user();
return $this->response()->success($user);
}
public function update(Request $request)
{
$user = auth('api')->user();
$service = UserService::make();
if ($service->update($user->id, $request->all())) {
return $this->response()->success('保存成功');
}
return $this->response()->fail($service->getError());
}
public function logout()
{
$user = auth('api')->user();
$user->currentAccessToken()->delete();
return $this->response()->success();
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers\Client;
use App\Admin\Services\UserService;
use App\Enums\SocialiteType;
use App\Exceptions\BaseException;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\UserSocialite;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'username' => 'required',
'password' => 'required',
]);
$user = User::where('phone', $request->input('username'))->first();
if (!$user) {
throw new BaseException('手机号不存在');
}
if (!Hash::check($request->input('password'), $user->password)) {
throw new BaseException('用户名或密码错误');
}
// 更新第三方账户
$openid = $request->input('openid');
$open_type = $request->input('open_type');
if ($openid && $open_type) {
UserSocialite::where(['openid' => $openid, 'type' => SocialiteType::from($open_type), 'user_type' => $user->getMorphClass()])->update([
'user_id' => $user->id,
]);
}
$token = $user->createToken('client')->plainTextToken;
return $this->response()->success(['token' => $token, 'user' => $user]);
}
public function register(Request $request)
{
$service = UserService::make();
if (!$service->store($request->all())) {
throw new BaseException($service->getError());
}
$user = User::where('phone', $request->input('phone'))->first();
// 更新第三方账户
$openid = $request->input('openid');
$open_type = $request->input('open_type');
if ($openid && $open_type) {
UserSocialite::where(['openid' => $openid, 'type' => SocialiteType::from($open_type), 'user_type' => $user->getMorphClass()])->update([
'user_id' => $user->id,
]);
}
$token = $user->createToken('client')->plainTextToken;
return $this->response()->success(['token' => $token, 'user' => $user]);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Client;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class PatientController extends Controller
{
public function index(Request $request)
{
$user = auth('api')->user();
$patients = $user->patients()->with(['type'])->get();
return $this->response()->success($patients);
}
public function show($id)
{
$user = auth('api')->user();
$patient = $user->patients()->with(['type'])->findOrFail($id);
return $this->response()->success($patient);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Client;
use App\Admin\Services\PatientRecordService;
use App\Http\Controllers\Controller;
use App\Models\PatientRecord;
use Illuminate\Http\Request;
class RecordController extends Controller
{
public function index(Request $request)
{
$user = auth('api')->user();
$list = PatientRecordService::make()->listQuery()->where('user_id', $user->id)->paginate($request->input('perPage'));
$items = $list->items();
$total = $list->total();
return $this->response()->success(compact('items', 'total'));
}
public function show($id)
{
$user = auth('api')->user();
$info = PatientRecordService::make()->listQuery()->where('user_id', $user->id)->findOrFail($id);
return $this->response()->success($info);
}
}

View File

@ -5,8 +5,14 @@ namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Slowlyo\OwlAdmin\Admin;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
protected function response()
{
return Admin::response();
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
class WebController extends Controller
{
public function upload(Request $request)
{
$path = $request->input('path', 'uploads').'/'.date('Y-m-d');
$result = [];
$disk = $this->getDisk();
// file 文件
$files = $request->except('path');
foreach ($files as $key => $fileData) {
$item = null;
if (is_array($fileData)) {
foreach ($fileData as $file) {
$item[] = $disk->url($this->saveFile($disk, $path, $file));
}
} else {
$item = $disk->url($this->saveFile($disk, $path, $fileData));
}
$result[$key] = $item;
}
return $this->response()->success($result);
}
protected function saveFile($disk, $path, $file = null)
{
$filePath = '';
if ($file instanceof UploadedFile) {
//限制文件类型:
if(in_array($file->getClientOriginalExtension(), [
'jpeg', 'jpg', 'gif', 'bmp', 'png', //图片
'mp4', //视频
'mp3', //音频
])){
$filePath = $disk->putFile($path, $file);
}
} elseif (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)) {
$type = $result[2];
if (in_array($type, ['jpeg', 'jpg', 'gif', 'bmp', 'png'])) {
$filePath = $path.'/'.uniqid().'.'.$type;
$disk->put($filePath, base64_decode(str_replace($result[1], '', $file)));
}
}
return $filePath;
}
protected function getDisk($disk = null): Filesystem
{
return Storage::disk($disk);
}
}

View File

@ -2,11 +2,13 @@
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use App\Models\UserSocialite;
use App\Enums\SocialiteType;
use Slowlyo\OwlAdmin\Admin;
use Overtrue\LaravelWeChat\EasyWeChat;
use Slowlyo\OwlAdmin\Models\AdminUser;
class WechatController extends Controller
{
@ -16,6 +18,7 @@ class WechatController extends Controller
$openid = $user->getId();
$socialite = UserSocialite::updateOrCreate([
'type' => SocialiteType::WxOfficial,
'user_type' => (new AdminUser)->getMorphClass(),
'openid' => $openid,
], [
'data' => $user,
@ -32,6 +35,27 @@ class WechatController extends Controller
return redirect(url('/h5/pages/index/welcome') . '?token=' . $token);
}
public function client()
{
$user = session('easywechat.oauth_user.default');
$openid = $user->getId();
$socialite = UserSocialite::updateOrCreate([
'type' => SocialiteType::WxOfficial,
'user_type' => (new User())->getMorphClass(),
'openid' => $openid,
], [
'data' => $user,
]);
$user = $socialite->user;
if (!$user) {
return redirect(url('/client/pages/login/index') . '?' . http_build_query(['openid' => $openid, 'open_type' => SocialiteType::WxOfficial->value]));
}
$token = $user->createToken('client')->plainTextToken;
return redirect(url('/client/pages/index/welcome') . '?token=' . $token);
}
public function officialAccessToken()
{
$app = EasyWeChat::officialAccount();

View File

@ -19,7 +19,7 @@ class PatientRecord extends Model
const ILLNESS_TYPE_KEY = 'illness_type';
protected $fillable = [
'patient_id', 'type_id', 'illness_type_id',
'patient_id', 'user_id', 'type_id', 'illness_type_id',
'treat_at', 'content', 'images',
'doctor_id', 'doctor_ratio', 'doctor_money',
'saler_id', 'saler_ratio', 'saler_money',
@ -46,6 +46,11 @@ class PatientRecord extends Model
return $this->belongsTo(Patient::class, 'patient_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function type()
{
return $this->belongsTo(Keyword::class, 'type_id');

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Casts\StorageFile;
use App\Enums\Gender;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
@ -13,7 +14,7 @@ class User extends Authenticate
{
use HasApiTokens, Filterable, HasDateTimeFormatter;
protected $fillable = ['address', 'birthday', 'name', 'password', 'phone', 'sex'];
protected $fillable = ['address', 'birthday', 'name', 'password', 'phone', 'sex', 'avatar'];
protected $appends = ['age', 'sex_text', 'birthday_format'];
@ -21,6 +22,7 @@ class User extends Authenticate
protected $casts = [
'sex' => Gender::class,
'avatar' => StorageFile::class,
'birthday' => 'date',
'password' => 'hashed',
];

View File

@ -11,6 +11,7 @@
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8",
"maatwebsite/excel": "^3.1",
"overtrue/easy-sms": "^2.5",
"overtrue/laravel-wechat": "^7.2",
"slowlyo/owl-admin": "^2.8",
"tucker-eric/eloquentfilter": "^3.2"

74
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "390fe261f07ff7ef9c30cfaa1ca4db93",
"content-hash": "0cebd4f46a575829eead0a4bf37bcea3",
"packages": [
{
"name": "brick/math",
@ -3127,6 +3127,78 @@
],
"time": "2021-05-12T11:11:27+00:00"
},
{
"name": "overtrue/easy-sms",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/overtrue/easy-sms.git",
"reference": "81d4deec69bbb6de6e5fdd7ab90cc933bd3e3046"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/easy-sms/zipball/81d4deec69bbb6de6e5fdd7ab90cc933bd3e3046",
"reference": "81d4deec69bbb6de6e5fdd7ab90cc933bd3e3046",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.2 || ^7.0",
"php": ">=5.6"
},
"require-dev": {
"brainmaestro/composer-git-hooks": "^2.8",
"jetbrains/phpstorm-attributes": "^1.0",
"mockery/mockery": "~1.3.3 || ^1.4.2",
"phpunit/phpunit": "^5.7 || ^7.5 || ^8.5.19 || ^9.5.8"
},
"type": "library",
"extra": {
"hooks": {
"pre-commit": [
"composer check-style",
"composer psalm",
"composer test"
],
"pre-push": [
"composer check-style"
]
}
},
"autoload": {
"psr-4": {
"Overtrue\\EasySms\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "overtrue",
"email": "i@overtrue.me"
}
],
"description": "The easiest way to send short message.",
"support": {
"issues": "https://github.com/overtrue/easy-sms/issues",
"source": "https://github.com/overtrue/easy-sms/tree/2.5.0"
},
"funding": [
{
"url": "https://github.com/overtrue",
"type": "github"
}
],
"time": "2023-08-07T07:51:17+00:00"
},
{
"name": "overtrue/laravel-wechat",
"version": "7.2.0",

View File

@ -40,6 +40,10 @@ return [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'sanctum',
'provider' => 'users',
],
],
/*

28
config/easysms.php 100644
View File

@ -0,0 +1,28 @@
<?php
return [
// HTTP 请求的超时时间(秒)
'timeout' => 5.0,
// 默认发送配置
'default' => [
// 网关调用策略,默认:顺序调用
'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,
// 默认可用的发送网关
'gateways' => [
'aliyun',
],
],
'gateways' => [
'errorlog' => [
'file' => 'storage/logs/easy-sms.log'
],
'aliyun' => [
'access_key_id' => env('ALIYUN_ACCESS_ID'),
'access_key_secret' => env('ALIYUN_ACCESS_SECRET'),
'sign_name' => env('ALIYUN_SMS_SIGN'),
]
]
];

View File

@ -30,6 +30,7 @@ class PatientRecordFactory extends Factory
$saler_ratio = data_get($type->options, 'saler_ratio', 0);
return [
'patient_id' => $patient->id,
'user_id' => $patient->user_id,
'type_id' => $type->id,
'illness_type_id' => Keyword::where('type_key', PatientRecord::ILLNESS_TYPE_KEY)->inRandomOrder()->value('id'),
'treat_at' => $faker->dateTimeBetween('-7 days'),

View File

@ -16,6 +16,7 @@ return new class extends Migration
$table->string('phone')->comment('联系方式')->unique();
$table->string('password')->comment('登录密码');
$table->string('name')->nullable()->comment('姓名');
$table->string('avatar')->nullable()->comment('头像');
$table->string('sex')->default(\App\Enums\Gender::None->value)->comment('性别');
$table->string('address')->nullable()->comment('地址');
$table->date('birthday')->nullable()->comment('出生年月');
@ -25,6 +26,10 @@ return new class extends Migration
Schema::table('patients', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->nullable();
});
Schema::table('patient_records', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->nullable();
});
}
/**
@ -36,5 +41,8 @@ return new class extends Migration
Schema::table('patients', function (Blueprint $table) {
$table->dropColumn('user_id');
});
Schema::table('patient_records', function (Blueprint $table) {
$table->dropColumn('user_id');
});
}
};

View File

@ -11,4 +11,5 @@ return [
'created_at' => '注册时间',
'password' => '登录密码',
'keyword' => '关键字',
'avatar' => '头像',
];

View File

@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /client/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /client/index.html [L]
</IfModule>

View File

@ -0,0 +1,2 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>宝芝堂</title><script>var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel="stylesheet" href="/client/static/index.5841170f.css"/><script defer="defer" src="/client/static/js/chunk-vendors.40c214d6.js"></script><script defer="defer" src="/client/static/js/index.9ce0c3de.js"></script></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(self["webpackChunkuniapp"]=self["webpackChunkuniapp"]||[]).push([[545],{3233:function(n,e,u){var r;u.r(e),u.d(e,{default:function(){return d}});var t,a=function(){var n=this,e=n.$createElement,u=n._self._c||e;return u("div")},i=[],c={data:function(){return{}},onLoad:function(n){n.token?(uni.setStorageSync("medical_record_client_auth_token",n.token),uni.reLaunch({url:"/pages/index/index"})):n.reLaunch?uni.reLaunch({url:n.reLaunch}):uni.reLaunch({url:"/pages/login/index"})}},l=c,o=u(1503),s=(0,o.Z)(l,a,i,!1,null,null,null,!1,r,t),d=s.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -13,4 +13,24 @@ use Illuminate\Support\Facades\Route;
| be assigned to the "api" middleware group. Make something great!
|
*/
Route::post('wechant/official-access-token', [\App\Http\Controllers\WechatController::class, 'officialAccessToken']);
Route::post('wechat/official-access-token', [\App\Http\Controllers\WechatController::class, 'officialAccessToken']);
Route::post('web/upload', [\App\Http\Controllers\WebController::class, 'upload']);
Route::group(['prefix' => 'client'], function () {
Route::post('login', [\App\Http\Controllers\Client\AuthController::class, 'login']);
Route::post('register', [\App\Http\Controllers\Client\AuthController::class, 'register']);
Route::group(['middleware' => 'auth:api'], function () {
Route::get('logout', [\App\Http\Controllers\Client\AccountController::class, 'logout']);
Route::get('user/profile', [\App\Http\Controllers\Client\AccountController::class, 'profile']);
Route::post('user/profile', [\App\Http\Controllers\Client\AccountController::class, 'update']);
Route::get('patient', [\App\Http\Controllers\Client\PatientController::class, 'index']);
Route::get('patient/{id}', [\App\Http\Controllers\Client\PatientController::class, 'show']);
Route::get('record', [\App\Http\Controllers\Client\RecordController::class, 'index']);
Route::get('record/{id}', [\App\Http\Controllers\Client\RecordController::class, 'show']);
});
});

View File

@ -17,4 +17,5 @@ Route::redirect('/', '/admin');
Route::group(['middleware' => [App\Http\Middleware\UserSocialite::class, 'easywechat.oauth:default,snsapi_base']], function () {
Route::get('oauth', [App\Http\Controllers\WechatController::class, 'oauth']);
Route::get('oauth/client', [App\Http\Controllers\WechatController::class, 'client']);
});

View File

@ -3,6 +3,7 @@
namespace Tests\Feature;
// use Illuminate\Foundation\Testing\RefreshDatabase;
use Overtrue\EasySms\EasySms;
use Tests\TestCase;
class ExampleTest extends TestCase
@ -12,8 +13,17 @@ class ExampleTest extends TestCase
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
$now = now();
dump($now->day, $now->month);
// try {
// $easySms = new EasySms(config('easysms'));
// $result = $easySms->send('18983405554', [
// 'template' => 'SMS_463637721',
// ]);
// dump($result);
// } catch (\Exception $e) {
// dump($e->getException('aliyun')->raw);
// }
$this->assertTrue(true);
}
}