feat: 第三阶段业务模块(服务产康/月嫂/进销存)

- 新增3个migration(15张表): service/nanny/inventory
- 新增15个Model + 13个Controller
- 新增55条API路由(总计212条)
- 新增3个前端API模块 + 11个管理页面
- vite build验证通过
This commit is contained in:
li
2026-03-13 20:29:45 +08:00
parent 16bcd9bcf0
commit e7d4d9b414
46 changed files with 2573 additions and 0 deletions
@@ -0,0 +1,96 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// 服务项目
Schema::create('service_items', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 100);
$table->tinyInteger('category')->nullable()->comment('1产康 2零售 3加餐 4其他');
$table->decimal('price', 10, 2);
$table->integer('duration')->nullable()->comment('服务时长(分钟)');
$table->text('description')->nullable();
$table->string('image', 255)->nullable();
$table->tinyInteger('status')->default(1);
$table->timestamps();
$table->index(['store_id', 'category']);
});
// 服务包
Schema::create('service_packages', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 100);
$table->json('items')->nullable()->comment('包含项目与次数');
$table->decimal('price', 10, 2);
$table->integer('validity_days')->nullable()->comment('有效天数');
$table->tinyInteger('status')->default(1);
$table->timestamps();
});
// 服务订单
Schema::create('service_orders', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->foreignId('customer_id')->constrained();
$table->string('order_no', 50)->unique();
$table->tinyInteger('type')->nullable()->comment('1单项 2服务包 3零售 4加餐');
$table->json('items')->nullable()->comment('订单项明细');
$table->decimal('total_amount', 12, 2)->nullable();
$table->decimal('discount_amount', 12, 2)->default(0);
$table->decimal('actual_amount', 12, 2)->nullable();
$table->tinyInteger('status')->default(0)->comment('0待付 1已付 2已完成 3已取消 4已退');
$table->tinyInteger('pay_method')->nullable()->comment('1微信 2支付宝 3现金 4挂账');
$table->timestamp('paid_at')->nullable();
$table->unsignedBigInteger('created_by')->nullable();
$table->timestamps();
$table->index('store_id');
$table->index('customer_id');
});
// 服务执行记录
Schema::create('service_executions', function (Blueprint $table) {
$table->id();
$table->foreignId('service_order_id')->constrained()->cascadeOnDelete();
$table->foreignId('service_item_id')->constrained();
$table->foreignId('customer_id')->constrained();
$table->unsignedBigInteger('technician_id')->nullable()->comment('技师/产康师');
$table->timestamp('scheduled_at')->nullable()->comment('预约时间');
$table->timestamp('started_at')->nullable();
$table->timestamp('ended_at')->nullable();
$table->tinyInteger('status')->default(0)->comment('0待执行 1执行中 2已完成 3已取消');
$table->text('remark')->nullable();
$table->timestamps();
$table->index('service_order_id');
$table->index('technician_id');
});
// 服务评价
Schema::create('service_reviews', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('service_order_id')->nullable();
$table->unsignedBigInteger('service_execution_id')->nullable();
$table->foreignId('customer_id')->constrained();
$table->tinyInteger('score')->nullable()->comment('1-5星');
$table->text('content')->nullable();
$table->timestamp('created_at')->nullable();
$table->index('service_order_id');
});
}
public function down(): void
{
Schema::dropIfExists('service_reviews');
Schema::dropIfExists('service_executions');
Schema::dropIfExists('service_orders');
Schema::dropIfExists('service_packages');
Schema::dropIfExists('service_items');
}
};
@@ -0,0 +1,84 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// 月嫂档案
Schema::create('nannies', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 50);
$table->string('phone', 20)->nullable();
$table->string('id_card', 20)->nullable();
$table->string('avatar', 255)->nullable();
$table->tinyInteger('level')->nullable()->comment('1初级 2中级 3高级 4金牌');
$table->json('skills')->nullable()->comment('技能标签');
$table->integer('experience_years')->nullable();
$table->string('health_cert', 255)->nullable()->comment('健康证');
$table->tinyInteger('cooperation_type')->nullable()->comment('1自有 2合作 3兼职');
$table->decimal('base_salary', 10, 2)->nullable();
$table->text('introduction')->nullable();
$table->tinyInteger('status')->default(1)->comment('1空闲 2服务中 3休假 4停用');
$table->timestamps();
$table->index(['store_id', 'status']);
});
// 月嫂订单
Schema::create('nanny_orders', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->foreignId('customer_id')->constrained();
$table->unsignedBigInteger('nanny_id')->nullable();
$table->string('order_no', 50)->unique();
$table->tinyInteger('service_type')->nullable()->comment('1月嫂 2育儿嫂');
$table->date('start_date')->nullable();
$table->date('end_date')->nullable();
$table->integer('days')->nullable();
$table->decimal('price', 12, 2)->nullable();
$table->tinyInteger('status')->default(0)->comment('0待匹配 1待确认 2已签约 3服务中 4已完成 5已取消');
$table->json('match_candidates')->nullable()->comment('匹配候选月嫂');
$table->text('remark')->nullable();
$table->unsignedBigInteger('created_by')->nullable();
$table->timestamps();
$table->index('store_id');
$table->index('nanny_id');
});
// 月嫂排班
Schema::create('nanny_schedules', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('nanny_id');
$table->unsignedBigInteger('nanny_order_id')->nullable();
$table->date('schedule_date');
$table->tinyInteger('type')->nullable()->comment('1服务 2休息 3培训');
$table->string('remark', 255)->nullable();
$table->timestamp('created_at')->nullable();
$table->index(['nanny_id', 'schedule_date']);
});
// 月嫂评价
Schema::create('nanny_reviews', function (Blueprint $table) {
$table->id();
$table->foreignId('nanny_order_id')->constrained()->cascadeOnDelete();
$table->foreignId('customer_id')->constrained();
$table->unsignedBigInteger('nanny_id');
$table->tinyInteger('score')->nullable()->comment('1-5');
$table->text('content')->nullable();
$table->timestamp('created_at')->nullable();
$table->index('nanny_id');
});
}
public function down(): void
{
Schema::dropIfExists('nanny_reviews');
Schema::dropIfExists('nanny_schedules');
Schema::dropIfExists('nanny_orders');
Schema::dropIfExists('nannies');
}
};
@@ -0,0 +1,106 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// 仓库
Schema::create('warehouses', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 50);
$table->unsignedBigInteger('manager_id')->nullable();
$table->tinyInteger('status')->default(1);
$table->timestamps();
});
// 供应商
Schema::create('suppliers', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 100);
$table->string('contact', 50)->nullable();
$table->string('phone', 20)->nullable();
$table->string('address', 255)->nullable();
$table->string('bank_info', 255)->nullable();
$table->tinyInteger('status')->default(1);
$table->timestamps();
});
// 物资(SKU
Schema::create('materials', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('name', 100);
$table->string('code', 50)->nullable()->comment('SKU编码');
$table->string('category', 50)->nullable();
$table->string('unit', 20)->nullable()->comment('单位');
$table->string('spec', 100)->nullable()->comment('规格');
$table->integer('min_stock')->default(0)->comment('安全库存');
$table->integer('shelf_life_days')->nullable()->comment('保质天数');
$table->tinyInteger('status')->default(1);
$table->timestamps();
$table->index('store_id');
$table->index('code');
});
// 库存
Schema::create('inventories', function (Blueprint $table) {
$table->id();
$table->foreignId('warehouse_id')->constrained();
$table->foreignId('material_id')->constrained();
$table->decimal('quantity', 12, 2)->default(0);
$table->string('batch_no', 50)->nullable();
$table->date('expire_date')->nullable();
$table->timestamp('updated_at')->nullable();
$table->unique(['warehouse_id', 'material_id', 'batch_no'], 'idx_wh_mat_batch');
});
// 采购单
Schema::create('purchase_orders', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->string('order_no', 50)->unique();
$table->unsignedBigInteger('supplier_id')->nullable();
$table->decimal('total_amount', 12, 2)->nullable();
$table->tinyInteger('status')->default(0)->comment('0草稿 1待审批 2已审批 3已入库 4已取消');
$table->json('items')->nullable()->comment('采购明细');
$table->unsignedBigInteger('audit_user_id')->nullable();
$table->timestamp('audit_at')->nullable();
$table->text('remark')->nullable();
$table->unsignedBigInteger('created_by')->nullable();
$table->timestamps();
});
// 出入库单
Schema::create('stock_movements', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained();
$table->foreignId('warehouse_id')->constrained();
$table->string('movement_no', 50)->unique();
$table->tinyInteger('type')->comment('1入库 2出库 3调拨 4盘点');
$table->tinyInteger('direction')->nullable()->comment('1入 2出');
$table->json('items')->nullable()->comment('物资明细');
$table->string('related_order', 50)->nullable()->comment('关联单号');
$table->tinyInteger('status')->default(0)->comment('0草稿 1已确认');
$table->unsignedBigInteger('operator_id')->nullable();
$table->text('remark')->nullable();
$table->timestamps();
$table->index(['store_id', 'type']);
});
}
public function down(): void
{
Schema::dropIfExists('stock_movements');
Schema::dropIfExists('purchase_orders');
Schema::dropIfExists('inventories');
Schema::dropIfExists('materials');
Schema::dropIfExists('suppliers');
Schema::dropIfExists('warehouses');
}
};