feat: 第四阶段财务与人事薪资模块

- 新增2个migration(11张表): finance(6表)/hr(5表)
- 新增11个Model + 11个Controller
- 新增50条API路由(总计262条)
- 新增2个前端API模块 + 10个管理页面
- 迁移运行通过, vite build验证通过
This commit is contained in:
li
2026-03-13 20:35:19 +08:00
parent e7d4d9b414
commit fb5e0daf1a
37 changed files with 3157 additions and 0 deletions
@@ -0,0 +1,118 @@
<?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('finance_categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('parent_id')->default(0)->index();
$table->string('name', 50);
$table->tinyInteger('type')->default(1)->comment('1=收入 2=支出');
$table->tinyInteger('level')->default(1);
$table->integer('sort')->default(0);
$table->tinyInteger('status')->default(1);
$table->timestamps();
});
// 收支记录
Schema::create('finance_records', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->string('record_no', 50)->unique();
$table->unsignedBigInteger('category_id')->nullable();
$table->tinyInteger('type')->comment('1=收入 2=支出');
$table->decimal('amount', 12, 2);
$table->tinyInteger('pay_method')->nullable()->comment('1=现金 2=微信 3=支付宝 4=银行');
$table->string('related_type', 50)->nullable()->comment('关联类型');
$table->unsignedBigInteger('related_id')->nullable();
$table->string('description')->nullable();
$table->tinyInteger('audit_status')->default(0)->comment('0=待审核 1=已通过 2=已驳回');
$table->unsignedBigInteger('audit_user_id')->nullable();
$table->timestamp('audit_at')->nullable();
$table->unsignedBigInteger('operator_id')->nullable();
$table->date('record_date');
$table->timestamps();
$table->index(['store_id', 'type']);
$table->index('record_date');
});
// 客户账户
Schema::create('customer_accounts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('customer_id')->index();
$table->decimal('cash_balance', 12, 2)->default(0);
$table->decimal('card_balance', 12, 2)->default(0);
$table->integer('points')->default(0);
$table->timestamps();
$table->unique(['store_id', 'customer_id']);
});
// 账户流水
Schema::create('account_transactions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('customer_account_id')->index();
$table->unsignedBigInteger('customer_id')->index();
$table->tinyInteger('type')->comment('1=充值 2=消费 3=退款 4=积分变动');
$table->decimal('amount', 12, 2);
$table->decimal('balance_after', 12, 2);
$table->string('related_type', 50)->nullable();
$table->unsignedBigInteger('related_id')->nullable();
$table->string('description')->nullable();
$table->unsignedBigInteger('operator_id')->nullable();
$table->timestamp('created_at')->nullable();
});
// 储值卡种
Schema::create('prepaid_cards', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->string('name', 100);
$table->decimal('face_value', 10, 2);
$table->decimal('sell_price', 10, 2);
$table->decimal('discount_rate', 5, 2)->default(1.00);
$table->integer('validity_days')->default(365);
$table->tinyInteger('status')->default(1);
$table->integer('sort')->default(0);
$table->timestamps();
});
// 发票
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->string('invoice_no', 50)->unique();
$table->unsignedBigInteger('customer_id')->nullable();
$table->tinyInteger('type')->default(1)->comment('1=电子 2=纸质');
$table->decimal('amount', 12, 2);
$table->decimal('tax_amount', 10, 2)->default(0);
$table->string('title')->nullable();
$table->string('tax_no', 50)->nullable();
$table->string('related_type', 50)->nullable();
$table->unsignedBigInteger('related_id')->nullable();
$table->tinyInteger('status')->default(0)->comment('0=待开 1=已开 2=已红冲');
$table->unsignedBigInteger('operator_id')->nullable();
$table->timestamp('issued_at')->nullable();
$table->timestamps();
$table->index(['store_id', 'status']);
});
}
public function down(): void
{
Schema::dropIfExists('invoices');
Schema::dropIfExists('prepaid_cards');
Schema::dropIfExists('account_transactions');
Schema::dropIfExists('customer_accounts');
Schema::dropIfExists('finance_records');
Schema::dropIfExists('finance_categories');
}
};
@@ -0,0 +1,108 @@
<?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('employee_profiles', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->string('employee_no', 30)->nullable();
$table->date('hire_date')->nullable();
$table->date('birth_date')->nullable();
$table->tinyInteger('gender')->default(0)->comment('0=未知 1=男 2=女');
$table->string('id_card', 20)->nullable();
$table->string('education', 30)->nullable();
$table->string('emergency_contact', 50)->nullable();
$table->string('emergency_phone', 20)->nullable();
$table->decimal('base_salary', 10, 2)->default(0);
$table->decimal('position_salary', 10, 2)->default(0);
$table->tinyInteger('status')->default(1)->comment('1=在职 2=试用 3=离职');
$table->date('left_date')->nullable();
$table->timestamps();
});
// 排班
Schema::create('schedules', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->date('schedule_date');
$table->tinyInteger('shift_type')->default(1)->comment('1=早班 2=中班 3=晚班 4=夜班 5=休息');
$table->time('start_time')->nullable();
$table->time('end_time')->nullable();
$table->timestamp('created_at')->nullable();
$table->index(['store_id', 'user_id', 'schedule_date']);
});
// 考勤记录
Schema::create('attendance_records', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->date('attendance_date');
$table->time('clock_in')->nullable();
$table->time('clock_out')->nullable();
$table->tinyInteger('status')->default(1)->comment('1=正常 2=迟到 3=早退 4=旷工 5=请假');
$table->decimal('overtime_hours', 5, 1)->default(0);
$table->string('remark')->nullable();
$table->timestamps();
$table->index(['store_id', 'user_id', 'attendance_date']);
});
// 请假申请
Schema::create('leave_requests', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->tinyInteger('type')->comment('1=年假 2=事假 3=病假 4=调休 5=产假 6=婚假');
$table->date('start_date');
$table->date('end_date');
$table->decimal('days', 4, 1);
$table->text('reason')->nullable();
$table->tinyInteger('status')->default(0)->comment('0=待审批 1=已批准 2=已拒绝');
$table->unsignedBigInteger('audit_user_id')->nullable();
$table->timestamp('audit_at')->nullable();
$table->timestamps();
});
// 工资记录
Schema::create('salary_records', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->string('year_month', 7)->comment('YYYY-MM');
$table->decimal('base_salary', 10, 2)->default(0);
$table->decimal('position_salary', 10, 2)->default(0);
$table->decimal('performance', 10, 2)->default(0);
$table->decimal('overtime_pay', 10, 2)->default(0);
$table->decimal('subsidy', 10, 2)->default(0);
$table->decimal('social_insurance', 10, 2)->default(0);
$table->decimal('housing_fund', 10, 2)->default(0);
$table->decimal('tax', 10, 2)->default(0);
$table->decimal('absence_deduction', 10, 2)->default(0);
$table->decimal('other_deduction', 10, 2)->default(0);
$table->decimal('actual_amount', 10, 2)->default(0);
$table->tinyInteger('status')->default(0)->comment('0=待确认 1=已确认 2=已发放');
$table->timestamp('confirmed_at')->nullable();
$table->timestamp('paid_at')->nullable();
$table->timestamps();
$table->unique(['store_id', 'user_id', 'year_month']);
});
}
public function down(): void
{
Schema::dropIfExists('salary_records');
Schema::dropIfExists('leave_requests');
Schema::dropIfExists('attendance_records');
Schema::dropIfExists('schedules');
Schema::dropIfExists('employee_profiles');
}
};