diff --git a/beike/Admin/Http/Controllers/RmaController.php b/beike/Admin/Http/Controllers/RmaController.php
new file mode 100644
index 00000000..6572d285
--- /dev/null
+++ b/beike/Admin/Http/Controllers/RmaController.php
@@ -0,0 +1,52 @@
+
+ * @created 2022-08-03 21:17:04
+ * @modified 2022-08-03 21:17:04
+ */
+
+namespace Beike\Admin\Http\Controllers;
+
+use Beike\Models\Rma;
+use Beike\Repositories\RmaReasonRepo;
+use Beike\Repositories\RmaRepo;
+use Exception;
+use Illuminate\Http\Request;
+
+class RmaController extends Controller
+{
+ public function index(Request $request)
+ {
+ $rmas = RmaRepo::list($request->only('name', 'email', 'telephone', 'product_name', 'model', 'type', 'status'));
+ $data = [
+ 'rmas' => $rmas,
+ ];
+
+ return view('admin::pages.rmas.index', $data);
+ }
+
+ /**
+ * @param int $id
+ * @return mixed
+ */
+ public function show(int $id)
+ {
+ $data = [
+ 'rma' => RmaRepo::find($id),
+ 'statuses' => RmaRepo::getStatuses(),
+ 'types' => RmaRepo::getTypes(),
+ ];
+ return view('admin::pages.rmas.info', $data);
+ }
+
+ public function destroy(int $id): array
+ {
+ RmaRepo::delete($id);
+
+ return json_success("已成功删除");
+ }
+}
diff --git a/beike/Admin/Http/Controllers/RmaReasonController.php b/beike/Admin/Http/Controllers/RmaReasonController.php
new file mode 100644
index 00000000..7aa6cd3f
--- /dev/null
+++ b/beike/Admin/Http/Controllers/RmaReasonController.php
@@ -0,0 +1,52 @@
+
+ * @created 2022-08-03 21:17:04
+ * @modified 2022-08-03 21:17:04
+ */
+
+namespace Beike\Admin\Http\Controllers;
+
+use Beike\Repositories\RmaReasonRepo;
+use Exception;
+use Illuminate\Http\Request;
+
+class RmaReasonController extends Controller
+{
+ public function index(Request $request)
+ {
+ $rmaReasons = RmaReasonRepo::list($request->only('name'));
+ $data = [
+ 'rmaReasons' => $rmaReasons,
+ ];
+
+ return view('admin::pages.rma_reasons.index', $data);
+ }
+
+ public function store(Request $request): array
+ {
+ $rmaReason = RmaReasonRepo::create($request->only('name'));
+ return json_success("创建成功", $rmaReason);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function update(Request $request, int $id): array
+ {
+ $rmaReason = RmaReasonRepo::update($id, $request->only('name'));
+
+ return json_success("成功修改", $rmaReason);
+ }
+
+ public function destroy(int $id): array
+ {
+ RmaReasonRepo::delete($id);
+
+ return json_success("已成功删除");
+ }
+}
diff --git a/beike/Admin/Http/Requests/RmaRequest.php b/beike/Admin/Http/Requests/RmaRequest.php
new file mode 100644
index 00000000..1b7c4049
--- /dev/null
+++ b/beike/Admin/Http/Requests/RmaRequest.php
@@ -0,0 +1,59 @@
+
+ * @created 2022-08-03 11:17:04
+ * @modified 2022-08-03 11:17:04
+ */
+
+namespace Beike\Admin\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class RmaRequest extends FormRequest
+{
+ /**
+ * Determine if the user is authorized to make this request.
+ *
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ $rules = [
+ 'order_id' => 'required|exists:orders,id',
+ 'order_product_id' => 'required|exists:order_products,id',
+ 'customer_id' => 'required|exists:customers,id',
+ 'quantity' => 'required',
+ 'opened' => 'required',
+ 'rma_reason_id' => 'required|exists:rma_reasons,id',
+ 'type' => 'required',
+ ];
+ return $rules;
+ }
+
+ public function attributes()
+ {
+ return [
+ 'order_id' => '订单',
+ 'order_product_id' => '订单商品',
+ 'customer_id' => '顾客',
+ 'quantity' => '数量',
+ 'opened' => '已拆包装',
+ 'rma_reason_id' => '退换货原因',
+ 'type' => '售后服务类型',
+ ];
+ }
+}
diff --git a/beike/Admin/Routes/admin.php b/beike/Admin/Routes/admin.php
index 8a7cb556..f531f943 100644
--- a/beike/Admin/Routes/admin.php
+++ b/beike/Admin/Routes/admin.php
@@ -72,6 +72,8 @@ Route::prefix($adminName)
Route::resource('products', Controllers\ProductController::class);
Route::resource('regions', Controllers\RegionController::class);
+ Route::resource('rmas', Controllers\RmaController::class);
+ Route::resource('rma_reasons', Controllers\RmaReasonController::class);
Route::get('settings', [Controllers\SettingController::class, 'index'])->name('settings.index');
Route::post('settings', [Controllers\SettingController::class, 'store'])->name('settings.store');
diff --git a/beike/Models/Rma.php b/beike/Models/Rma.php
new file mode 100644
index 00000000..7cc322f8
--- /dev/null
+++ b/beike/Models/Rma.php
@@ -0,0 +1,42 @@
+
+ * @created 2022-08-01 20:22:18
+ * @modified 2022-08-01 20:22:18
+ */
+
+namespace Beike\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class Rma extends Base
+{
+ use HasFactory;
+
+ protected $fillable = ['order_id','order_product_id','customer_id','name','email','telephone','product_name','model','quantity','opened','rma_reason_id','type','status','comment'];
+
+ public function order() :BelongsTo
+ {
+ return $this->belongsTo(Order::Class);
+ }
+
+ public function orderProduct() :BelongsTo
+ {
+ return $this->belongsTo(OrderProduct::class);
+ }
+
+ public function customer() :BelongsTo
+ {
+ return $this->belongsTo(Customer::class);
+ }
+
+ public function reason() :BelongsTo
+ {
+ return $this->belongsTo(RmaReason::class);
+ }
+}
diff --git a/beike/Models/RmaReason.php b/beike/Models/RmaReason.php
new file mode 100644
index 00000000..233db784
--- /dev/null
+++ b/beike/Models/RmaReason.php
@@ -0,0 +1,22 @@
+
+ * @created 2022-08-01 20:22:18
+ * @modified 2022-08-01 20:22:18
+ */
+
+namespace Beike\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+
+class RmaReason extends Base
+{
+ use HasFactory;
+
+ protected $fillable = ['locale','name'];
+}
+
diff --git a/beike/Repositories/RmaReasonRepo.php b/beike/Repositories/RmaReasonRepo.php
new file mode 100644
index 00000000..f03717cd
--- /dev/null
+++ b/beike/Repositories/RmaReasonRepo.php
@@ -0,0 +1,88 @@
+
+ * @created 2022-08-01 20:42:05
+ * @modified 2022-08-01 20:42:05
+ */
+
+namespace Beike\Repositories;
+
+use Beike\Models\Rma;
+use Beike\Models\RmaReason;
+use Exception;
+use Illuminate\Contracts\Pagination\LengthAwarePaginator;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Model;
+
+class RmaReasonRepo
+{
+ /**
+ * 创建一个记录
+ * @param $data
+ * @return Builder|Model
+ */
+ public static function create($data)
+ {
+ $item = RmaReason::query()->create($data);
+ return $item;
+ }
+
+ /**
+ * @param $reason
+ * @param $data
+ * @return Builder|Builder[]|Collection|Model|mixed
+ * @throws Exception
+ */
+ public static function update($reason, $data)
+ {
+ if (!$reason instanceof RmaReason) {
+ $reason = RmaReason::query()->find($reason);
+ }
+ if (!$reason) {
+ throw new Exception("退换货原因id $reason 不存在");
+ }
+ $reason->update($data);
+ return $reason;
+ }
+
+ /**
+ * @param $id
+ * @return Builder|Builder[]|Collection|Model|null
+ */
+ public static function find($id)
+ {
+ return RmaReason::query()->find($id);
+ }
+
+ /**
+ * @param $id
+ * @return void
+ */
+ public static function delete($id)
+ {
+ $reason = RmaReason::query()->find($id);
+ if ($reason) {
+ $reason->delete();
+ }
+ }
+
+ /**
+ * @param $data
+ * @return LengthAwarePaginator
+ */
+ public static function list($data = []): LengthAwarePaginator
+ {
+ $builder = Rma::query();
+
+ if (isset($data['name'])) {
+ $builder->where('name', 'like', "%{$data['name']}%");
+ }
+
+ return $builder->paginate(10);
+ }
+}
diff --git a/beike/Repositories/RmaRepo.php b/beike/Repositories/RmaRepo.php
new file mode 100644
index 00000000..6ce13a1d
--- /dev/null
+++ b/beike/Repositories/RmaRepo.php
@@ -0,0 +1,147 @@
+
+ * @created 2022-08-01 20:42:05
+ * @modified 2022-08-01 20:42:05
+ */
+
+namespace Beike\Repositories;
+
+use Beike\Models\Rma;
+use Beike\Models\Customer;
+use Beike\Shop\Http\Resources\RmaDetail;
+use Exception;
+use Illuminate\Contracts\Pagination\LengthAwarePaginator;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Model;
+
+class RmaRepo
+{
+ /**
+ * 创建一个记录
+ * @param $data
+ * @return Builder|Model
+ */
+ public static function create($data)
+ {
+ $item = Rma::query()->create($data);
+ return $item;
+ }
+
+ /**
+ * @param $rma
+ * @param $data
+ * @return Builder|Builder[]|Collection|Model|mixed
+ * @throws Exception
+ */
+ public static function update($rma, $data)
+ {
+ if (!$rma instanceof Rma) {
+ $rma = Rma::query()->find($rma);
+ }
+ if (!$rma) {
+ throw new Exception("退还/售后id $rma 不存在");
+ }
+ $rma->update($data);
+ return $rma;
+ }
+
+ /**
+ * @param $id
+ * @return Builder|Builder[]|Collection|Model|null
+ */
+ public static function find($id)
+ {
+ return Rma::query()->find($id);
+ }
+
+ /**
+ * @param $id
+ * @return void
+ */
+ public static function delete($id)
+ {
+ $rma = Rma::query()->find($id);
+ if ($rma) {
+ $rma->delete();
+ }
+ }
+
+ /**
+ * @param $data
+ * @return LengthAwarePaginator
+ */
+ public static function list($data): LengthAwarePaginator
+ {
+ $builder = Rma::query();
+
+ if (isset($data['name'])) {
+ $builder->where('name', 'like', "%{$data['name']}%");
+ }
+ if (isset($data['email'])) {
+ $builder->where('email', 'like', "%{$data['email']}%");
+ }
+ if (isset($data['telephone'])) {
+ $builder->where('telephone', 'like', "%{$data['telephone']}%");
+ }
+ if (isset($data['product_name'])) {
+ $builder->where('product_name', 'like', "%{$data['product_name']}%");
+ }
+ if (isset($data['model'])) {
+ $builder->where('model', 'like', "%{$data['model']}%");
+ }
+ if (isset($data['type'])) {
+ $builder->where('type', $data['type']);
+ }
+ if (isset($data['status'])) {
+ $builder->where('status', $data['status']);
+ }
+
+ return $builder->paginate(10)->withQueryString();
+ }
+
+ /**
+ * @param $customer
+ * @return array
+ */
+ public static function listByCustomer($customer): array
+ {
+ if (!$customer instanceof Customer) {
+ $customer = CustomerRepo::find($customer->id);
+ }
+
+ $results = [];
+ foreach ($customer->rmas as $rma) {
+ $results[$rma->first][] = (new RmaDetail($rma))->jsonSerialize();
+ }
+
+ return $results;
+ }
+
+ public static function getStatuses(): array
+ {
+ return [
+ 'pending' => '待处理',
+ 'rejected' => '已拒绝',
+ 'approved' => '已批准(待顾客寄回商品)',
+ 'shipped' => '已发货(寄回商品)',
+ 'completed' => '已完成'
+ ];
+ }
+
+ public static function getTypes(): array
+ {
+ return [
+ 'return' => '退货',
+ 'exchange' => '换货',
+ 'repair' => '维修',
+ 'reissue' => '补发商品',
+ 'refund' => '仅退款'
+ ];
+ }
+}
diff --git a/beike/Shop/Http/Resources/RmaDetail.php b/beike/Shop/Http/Resources/RmaDetail.php
new file mode 100644
index 00000000..cf569eaa
--- /dev/null
+++ b/beike/Shop/Http/Resources/RmaDetail.php
@@ -0,0 +1,22 @@
+
+ * @created 2022-08-03 10:33:06
+ * @modified 2022-08-03 10:33:06
+ */
+
+namespace Beike\Shop\Http\Resources;
+
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class RmaDetail extends JsonResource
+{
+ public function toArray($request): array
+ {
+ return $this->toArray($request);
+ }
+}
diff --git a/database/migrations/2022_07_29_021003_return_table.php b/database/migrations/2022_07_29_021003_return_table.php
new file mode 100644
index 00000000..d9a27d96
--- /dev/null
+++ b/database/migrations/2022_07_29_021003_return_table.php
@@ -0,0 +1,60 @@
+id();
+ $table->unsignedInteger('order_id');
+ $table->unsignedInteger('order_product_id');
+ $table->unsignedInteger('customer_id');
+ $table->string('name');
+ $table->string('email');
+ $table->string('telephone');
+ $table->string('product_name');
+ $table->string('model');
+ $table->integer('quantity');
+ $table->tinyInteger('opened');
+ $table->unsignedInteger('rma_reason_id');
+ $table->string('type'); // 售后服务类型:退货、换货、维修、补发商品、仅退款
+ $table->string('status'); //
+ $table->text('comment');
+ $table->timestamps();
+ });
+ Schema::create('rma_histories', function (Blueprint $table) {
+ $table->id();
+ $table->unsignedInteger('rma_id');
+ $table->string('status');
+ $table->tinyInteger('notify');
+ $table->text('comment');
+ $table->timestamps();
+ });
+ Schema::create('rma_reasons', function (Blueprint $table) {
+ $table->id();
+ $table->string('locale');
+ $table->string('name');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @rma void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('rmas');
+ Schema::dropIfExists('rma_histories');
+ Schema::dropIfExists('rma_reasons');
+ }
+}
diff --git a/resources/beike/admin/views/pages/rma_reasons/index.blade.php b/resources/beike/admin/views/pages/rma_reasons/index.blade.php
new file mode 100644
index 00000000..20ab4408
--- /dev/null
+++ b/resources/beike/admin/views/pages/rma_reasons/index.blade.php
@@ -0,0 +1,39 @@
+@extends('admin::layouts.master')
+
+@section('title', '售后申请列表')
+
+@section('content')
+
+
+
+
+
+
+
+
+ | ID |
+ 名称 |
+ 操作 |
+
+
+
+ @foreach ($rmaReasons as $rmaReason)
+
+ | {{ $rmaReason->id }} |
+ {{ $rmaReason->name }} |
+ 查看 |
+
+ @endforeach
+
+
+
+ {{ $rmaReasons->links('admin::vendor/pagination/bootstrap-4') }}
+
+
+@endsection
+
+@push('footer')
+
+@endpush
diff --git a/resources/beike/admin/views/pages/rmas/index.blade.php b/resources/beike/admin/views/pages/rmas/index.blade.php
new file mode 100644
index 00000000..24ce82db
--- /dev/null
+++ b/resources/beike/admin/views/pages/rmas/index.blade.php
@@ -0,0 +1,48 @@
+@extends('admin::layouts.master')
+
+@section('title', '售后申请列表')
+
+@section('content')
+
+
+
+
+
+ | 客户姓名 |
+ 邮箱 |
+ 电话号码 |
+ 商品 |
+ 商品型号 |
+ 数量 |
+ 服务类型 |
+ 状态 |
+ 操作 |
+
+
+
+ @foreach ($rmas as $rma)
+
+ | {{ $rma->name }} |
+ {{ $rma->email }} |
+ {{ $rma->telephone }} |
+ {{ $rma->product_name }} |
+ {{ $rma->model }} |
+ {{ $rma->quantity }} |
+ {{ $rma->type }} |
+ {{ $rma->status }} |
+ 查看 |
+
+ @endforeach
+
+
+
+ {{ $rmas->links('admin::vendor/pagination/bootstrap-4') }}
+
+
+@endsection
+
+@push('footer')
+
+@endpush
diff --git a/resources/beike/admin/views/pages/rmas/info.blade.php b/resources/beike/admin/views/pages/rmas/info.blade.php
new file mode 100644
index 00000000..0439e833
--- /dev/null
+++ b/resources/beike/admin/views/pages/rmas/info.blade.php
@@ -0,0 +1,62 @@
+@extends('admin::layouts.master')
+
+@section('title', '售后申请')
+
+@section('content')
+
+
+
+
+
+
+
+
+ | ID: |
+ {{ $rma->id }} |
+
+
+ | 客户姓名: |
+ {{ $rma->name }} |
+
+
+ | 联系电话: |
+ {{ $rma->telephone }} |
+
+
+
+
+
+
+
+
+ | 商品: |
+ {{ $rma->product_name }} |
+
+
+ | 型号: |
+ {{ $rma->model }} |
+
+
+ | 数量: |
+ {{ $rma->quantity }} |
+
+
+ | 退货原因: |
+ {{ $rma->quantity }} |
+
+
+
+
+
+
+
+
+
+@endsection
+
+