feat: 完善前后端核心功能模块
后端: - 新增认证(auth)、任务(tasks)、映射(mappings)、对账(reconciliation)、异常(exceptions)、导出(exports) API - 新增核心模块: database, security, permissions, tenant, exceptions, error_handlers - 新增数据模型: user, company, reconciliation_task, field_mapping, uploaded_file 等 - 新增服务层: ai_recognizer, file_parser, file_storage, mapping, reconciliation 等 - 添加数据库迁移脚本 前端: - 新增登录页面和仪表盘页面 - 新增任务列表、任务详情、字段映射页面 - 新增异常处理页面和规则设置页面 - 新增 API 代理路由 /api/[...path] - 新增 UI 组件库 (button, card, dialog, input, table 等) - 新增 auth 组件 (ProtectedRoute, PermissionGate) - 新增 layout 组件 (Header, Sidebar) - 新增 mapping 组件 (FieldMappingTable, AISuggestionPanel) - 新增 API 客户端和 hooks (useAsync, useToast, usePermission 等) - 新增状态管理 (auth-store, company-store, ui-store) - 集成 Tailwind CSS 和 shadcn/ui 组件库 其他: - 添加 Alembic 数据库迁移配置 - 添加初始化示例数据脚本 - 更新项目文档
This commit is contained in:
@@ -0,0 +1,556 @@
|
||||
"""
|
||||
示例数据初始化脚本
|
||||
|
||||
创建一套完整的真实业务场景示例数据:
|
||||
- 企业:深圳星辰科技有限公司
|
||||
- 用户:admin 管理员
|
||||
- 3个月工资对账数据(2026年4月、5月、6月)
|
||||
- 包含各类业务异常场景
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
import random
|
||||
|
||||
from app.core.database import async_session_maker as AsyncSessionLocal
|
||||
from app.core.security import hash_password
|
||||
|
||||
# 导入所有模型以初始化 SQLAlchemy 关系
|
||||
from app.models.company import Company, CompanyPlan, CompanyStatus
|
||||
from app.models.user import User, UserRole, UserStatus
|
||||
from app.models.uploaded_file import UploadedFile, FileType, ParseStatus
|
||||
from app.models.reconciliation_task import ReconciliationTask, TaskStatus
|
||||
from app.models.exception_item import ExceptionItem, ExceptionType, ExceptionStatus, ExceptionSeverity
|
||||
from app.models.field_mapping import FieldMapping
|
||||
from app.models.company_rule import CompanyRule
|
||||
from app.models.standard_field import StandardField
|
||||
|
||||
|
||||
# ========== 基础数据定义 ==========
|
||||
|
||||
# 公司信息
|
||||
COMPANY_DATA = {
|
||||
"name": "深圳星辰科技有限公司",
|
||||
"plan": CompanyPlan.PROFESSIONAL.value,
|
||||
"status": CompanyStatus.ACTIVE.value,
|
||||
"data_retention_months": 12,
|
||||
"max_users": 20,
|
||||
"is_trial": False,
|
||||
}
|
||||
|
||||
# 用户列表
|
||||
USERS_DATA = [
|
||||
{
|
||||
"email": "admin@xingchen.com",
|
||||
"password": "admin123",
|
||||
"full_name": "张明华",
|
||||
"role": UserRole.ADMIN.value,
|
||||
},
|
||||
{
|
||||
"email": "finance@xingchen.com",
|
||||
"password": "finance123",
|
||||
"full_name": "李晓燕",
|
||||
"role": UserRole.FINANCE_MANAGER.value,
|
||||
},
|
||||
{
|
||||
"email": "accountant@xingchen.com",
|
||||
"password": "account123",
|
||||
"full_name": "王建国",
|
||||
"role": UserRole.ACCOUNTANT.value,
|
||||
},
|
||||
]
|
||||
|
||||
# 员工数据(25名员工)
|
||||
EMPLOYEES = [
|
||||
# 管理层
|
||||
{"id": "EMP001", "name": "张明华", "department": "管理层", "position": "总经理", "base_salary": 35000},
|
||||
{"id": "EMP002", "name": "李晓燕", "department": "财务部", "position": "财务总监", "base_salary": 28000},
|
||||
{"id": "EMP003", "name": "王建国", "department": "技术部", "position": "技术总监", "base_salary": 30000},
|
||||
|
||||
# 技术部
|
||||
{"id": "EMP004", "name": "刘伟强", "department": "技术部", "position": "高级工程师", "base_salary": 22000},
|
||||
{"id": "EMP005", "name": "陈志明", "department": "技术部", "position": "工程师", "base_salary": 18000},
|
||||
{"id": "EMP006", "name": "周建军", "department": "技术部", "position": "工程师", "base_salary": 17000},
|
||||
{"id": "EMP007", "name": "吴海涛", "department": "技术部", "position": "初级工程师", "base_salary": 12000},
|
||||
{"id": "EMP008", "name": "郑晓峰", "department": "技术部", "position": "测试工程师", "base_salary": 15000},
|
||||
|
||||
# 产品部
|
||||
{"id": "EMP009", "name": "孙丽娜", "department": "产品部", "position": "产品经理", "base_salary": 22000},
|
||||
{"id": "EMP010", "name": "马俊杰", "department": "产品部", "position": "产品专员", "base_salary": 14000},
|
||||
{"id": "EMP011", "name": "朱婷婷", "department": "产品部", "position": "UI设计师", "base_salary": 16000},
|
||||
|
||||
# 运营部
|
||||
{"id": "EMP012", "name": "胡文静", "department": "运营部", "position": "运营总监", "base_salary": 25000},
|
||||
{"id": "EMP013", "name": "林浩然", "department": "运营部", "position": "运营专员", "base_salary": 13000},
|
||||
{"id": "EMP014", "name": "何雨晴", "department": "运营部", "position": "客服主管", "base_salary": 12000},
|
||||
{"id": "EMP015", "name": "高建峰", "department": "运营部", "position": "客服专员", "base_salary": 9000},
|
||||
|
||||
# 市场部
|
||||
{"id": "EMP016", "name": "罗晓东", "department": "市场部", "position": "市场总监", "base_salary": 26000},
|
||||
{"id": "EMP017", "name": "宋志远", "department": "市场部", "position": "市场专员", "base_salary": 11000},
|
||||
{"id": "EMP018", "name": "唐思远", "department": "市场部", "position": "销售经理", "base_salary": 15000},
|
||||
{"id": "EMP019", "name": "韩冰冰", "department": "市场部", "position": "销售代表", "base_salary": 8000},
|
||||
|
||||
# 行政部
|
||||
{"id": "EMP020", "name": "冯婉君", "department": "行政部", "position": "行政经理", "base_salary": 14000},
|
||||
{"id": "EMP021", "name": "许志刚", "department": "行政部", "position": "行政专员", "base_salary": 8000},
|
||||
{"id": "EMP022", "name": "邓小丽", "department": "行政部", "position": "前台接待", "base_salary": 6000},
|
||||
|
||||
# 人事部
|
||||
{"id": "EMP023", "name": "曹丽华", "department": "人事部", "position": "人事经理", "base_salary": 15000},
|
||||
{"id": "EMP024", "name": "彭海燕", "department": "人事部", "position": "招聘专员", "base_salary": 9000},
|
||||
{"id": "EMP025", "name": "曾敏仪", "department": "人事部", "position": "薪酬专员", "base_salary": 8500},
|
||||
]
|
||||
|
||||
|
||||
def calculate_salary(employee: dict, month: int, year: int, has_bonus: bool = True) -> dict:
|
||||
"""计算员工月工资"""
|
||||
base = employee["base_salary"]
|
||||
|
||||
# 绩效工资(基本工资的10%-30%)
|
||||
performance = base * random.uniform(0.1, 0.3)
|
||||
|
||||
# 奖金(部分员工有)
|
||||
bonus = base * random.uniform(0.5, 1.5) if has_bonus and random.random() > 0.3 else 0
|
||||
|
||||
# 加班费(随机)
|
||||
overtime = random.choice([0, 500, 1000, 1500, 2000])
|
||||
|
||||
# 应发工资
|
||||
gross_salary = base + performance + bonus + overtime
|
||||
|
||||
# 社保个人部分(基本工资的10.5%)
|
||||
social_security = base * 0.105
|
||||
|
||||
# 公积金(基本工资的12%)
|
||||
housing_fund = base * 0.12
|
||||
|
||||
# 个税专项附加扣除
|
||||
tax_deduction = 2000 # 子女教育 + 继续教育 + 住房租金 等
|
||||
|
||||
# 应税工资
|
||||
taxable = gross_salary - social_security - housing_fund - tax_deduction
|
||||
|
||||
# 个税(简化计算)
|
||||
if taxable <= 3000:
|
||||
tax = taxable * 0.03
|
||||
elif taxable <= 12000:
|
||||
tax = taxable * 0.10 - 210
|
||||
elif taxable <= 25000:
|
||||
tax = taxable * 0.20 - 1410
|
||||
elif taxable <= 35000:
|
||||
tax = taxable * 0.25 - 2660
|
||||
elif taxable <= 55000:
|
||||
tax = taxable * 0.30 - 4410
|
||||
elif taxable <= 80000:
|
||||
tax = taxable * 0.35 - 7160
|
||||
else:
|
||||
tax = taxable * 0.45 - 15160
|
||||
|
||||
tax = max(0, tax)
|
||||
|
||||
# 实发工资
|
||||
net_salary = gross_salary - social_security - housing_fund - tax
|
||||
|
||||
return {
|
||||
"employee_id": employee["id"],
|
||||
"employee_name": employee["name"],
|
||||
"department": employee["department"],
|
||||
"base_salary": round(base, 2),
|
||||
"performance_salary": round(performance, 2),
|
||||
"bonus": round(bonus, 2),
|
||||
"overtime_pay": round(overtime, 2),
|
||||
"gross_salary": round(gross_salary, 2),
|
||||
"social_security": round(social_security, 2),
|
||||
"housing_fund": round(housing_fund, 2),
|
||||
"personal_income_tax": round(tax, 2),
|
||||
"net_salary": round(net_salary, 2),
|
||||
}
|
||||
|
||||
|
||||
def calculate_social_security(employee: dict) -> dict:
|
||||
"""计算社保"""
|
||||
base = employee["base_salary"]
|
||||
|
||||
# 养老保险(个人8%)
|
||||
pension = base * 0.08
|
||||
|
||||
# 医疗保险(个人2%)
|
||||
medical = base * 0.02
|
||||
|
||||
# 失业保险(个人0.5%)
|
||||
unemployment = base * 0.005
|
||||
|
||||
# 工伤保险(个人0%)
|
||||
work_injury = 0
|
||||
|
||||
# 生育保险(个人0%)
|
||||
maternity = 0
|
||||
|
||||
total = pension + medical + unemployment
|
||||
|
||||
return {
|
||||
"employee_id": employee["id"],
|
||||
"employee_name": employee["name"],
|
||||
"department": employee["department"],
|
||||
"pension": round(pension, 2),
|
||||
"medical": round(medical, 2),
|
||||
"unemployment": round(unemployment, 2),
|
||||
"work_injury": round(work_injury, 2),
|
||||
"maternity": round(maternity, 2),
|
||||
"total": round(total, 2),
|
||||
}
|
||||
|
||||
|
||||
def calculate_tax(employee: dict, gross_salary: float) -> dict:
|
||||
"""计算个税"""
|
||||
base = employee["base_salary"]
|
||||
|
||||
# 社保个人部分
|
||||
social = base * 0.105
|
||||
|
||||
# 公积金
|
||||
housing = base * 0.12
|
||||
|
||||
# 专项附加扣除
|
||||
deduction = 2000
|
||||
|
||||
# 应税所得
|
||||
taxable = gross_salary - social - housing - deduction - 5000 # 起征点
|
||||
|
||||
if taxable <= 0:
|
||||
tax = 0
|
||||
tax_rate = 0
|
||||
quick_deduction = 0
|
||||
elif taxable <= 3000:
|
||||
tax = taxable * 0.03
|
||||
tax_rate = 0.03
|
||||
quick_deduction = 0
|
||||
elif taxable <= 12000:
|
||||
tax = taxable * 0.10 - 210
|
||||
tax_rate = 0.10
|
||||
quick_deduction = 210
|
||||
elif taxable <= 25000:
|
||||
tax = taxable * 0.20 - 1410
|
||||
tax_rate = 0.20
|
||||
quick_deduction = 1410
|
||||
elif taxable <= 35000:
|
||||
tax = taxable * 0.25 - 2660
|
||||
tax_rate = 0.25
|
||||
quick_deduction = 2660
|
||||
elif taxable <= 55000:
|
||||
tax = taxable * 0.30 - 4410
|
||||
tax_rate = 0.30
|
||||
quick_deduction = 4410
|
||||
elif taxable <= 80000:
|
||||
tax = taxable * 0.35 - 7160
|
||||
tax_rate = 0.35
|
||||
quick_deduction = 7160
|
||||
else:
|
||||
tax = taxable * 0.45 - 15160
|
||||
tax_rate = 0.45
|
||||
quick_deduction = 15160
|
||||
|
||||
tax = max(0, tax)
|
||||
|
||||
return {
|
||||
"employee_id": employee["id"],
|
||||
"employee_name": employee["name"],
|
||||
"department": employee["department"],
|
||||
"gross_salary": round(gross_salary, 2),
|
||||
"taxable_income": round(taxable, 2),
|
||||
"tax_rate": tax_rate,
|
||||
"quick_deduction": quick_deduction,
|
||||
"tax_amount": round(tax, 2),
|
||||
}
|
||||
|
||||
|
||||
async def init_sample_data():
|
||||
"""初始化示例数据"""
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
# 1. 创建公司
|
||||
print("创建公司...")
|
||||
company = Company(**COMPANY_DATA)
|
||||
db.add(company)
|
||||
await db.flush()
|
||||
|
||||
# 2. 创建用户
|
||||
print("创建用户...")
|
||||
admin_user = None
|
||||
for user_data in USERS_DATA:
|
||||
user = User(
|
||||
company_id=company.id,
|
||||
email=user_data["email"],
|
||||
hashed_password=hash_password(user_data["password"]),
|
||||
full_name=user_data["full_name"],
|
||||
role=user_data["role"],
|
||||
status=UserStatus.ACTIVE.value,
|
||||
)
|
||||
db.add(user)
|
||||
if user_data["role"] == UserRole.ADMIN.value:
|
||||
admin_user = user
|
||||
await db.flush()
|
||||
|
||||
# 3. 创建3个月的对账任务
|
||||
print("创建对账任务...")
|
||||
tasks = []
|
||||
months = [
|
||||
(2026, 4, "4月"),
|
||||
(2026, 5, "5月"),
|
||||
(2026, 6, "6月"),
|
||||
]
|
||||
|
||||
for year, month, month_name in months:
|
||||
task = ReconciliationTask(
|
||||
company_id=company.id,
|
||||
name=f"{year}年{month_name}工资对账",
|
||||
period=f"{year}-{month:02d}",
|
||||
status=TaskStatus.COMPLETED.value,
|
||||
created_by=admin_user.id,
|
||||
total_employees=len(EMPLOYEES),
|
||||
)
|
||||
db.add(task)
|
||||
await db.flush()
|
||||
tasks.append((task, year, month))
|
||||
|
||||
# 4. 为每个月创建异常
|
||||
print("创建异常数据...")
|
||||
|
||||
# 10月异常
|
||||
task_10, year_10, month_10 = tasks[0]
|
||||
task_10.matched_count = 23
|
||||
task_10.exception_count = 2
|
||||
|
||||
# 异常1:新员工入职(工资表有,社保表无)
|
||||
exc1 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_10.id,
|
||||
exception_type=ExceptionType.MISSING_EMPLOYEE.value,
|
||||
severity=ExceptionSeverity.MEDIUM.value,
|
||||
status=ExceptionStatus.PENDING.value,
|
||||
employee_id="EMP026",
|
||||
employee_name="新入职员工",
|
||||
description="员工【新入职员工】在工资表中存在,但在社保表中未找到记录",
|
||||
detail={"source": "工资表", "missing_in": "社保表"},
|
||||
ai_suggestion="请确认该员工是否已完成社保增员,如已增员请重新上传社保表",
|
||||
)
|
||||
db.add(exc1)
|
||||
|
||||
# 异常2:金额不匹配
|
||||
salary_12 = calculate_salary(EMPLOYEES[11], month_10, year_10) # 胡文静
|
||||
tax_12 = calculate_tax(EMPLOYEES[11], salary_12["gross_salary"])
|
||||
exc2 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_10.id,
|
||||
exception_type=ExceptionType.AMOUNT_MISMATCH.value,
|
||||
severity=ExceptionSeverity.HIGH.value,
|
||||
status=ExceptionStatus.PENDING.value,
|
||||
employee_id=EMPLOYEES[11]["id"],
|
||||
employee_name=EMPLOYEES[11]["name"],
|
||||
description=f"员工【{EMPLOYEES[11]['name']}】工资表实发金额与个税表申报金额不一致",
|
||||
detail={
|
||||
"salary_net": salary_12["net_salary"],
|
||||
"tax_declared": tax_12["tax_amount"],
|
||||
"difference": abs(salary_12["net_salary"] - tax_12["tax_amount"]),
|
||||
},
|
||||
salary_amount=salary_12["net_salary"],
|
||||
tax_amount=tax_12["tax_amount"],
|
||||
difference_amount=abs(salary_12["net_salary"] - tax_12["tax_amount"]),
|
||||
ai_suggestion="请核对工资表和个税申报表的数据,确认是计算错误还是申报错误",
|
||||
)
|
||||
db.add(exc2)
|
||||
|
||||
# 11月异常
|
||||
task_11, year_11, month_11 = tasks[1]
|
||||
task_11.matched_count = 24
|
||||
task_11.exception_count = 1
|
||||
|
||||
# 异常3:离职员工(工资表无,社保表有)
|
||||
exc3 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_11.id,
|
||||
exception_type=ExceptionType.MISSING_EMPLOYEE.value,
|
||||
severity=ExceptionSeverity.MEDIUM.value,
|
||||
status=ExceptionStatus.CONFIRMED.value,
|
||||
employee_id="EMP007",
|
||||
employee_name="吴海涛",
|
||||
description="员工【吴海涛】在社保表中存在,但在本月工资表中未找到",
|
||||
detail={"source": "社保表", "missing_in": "工资表"},
|
||||
handled_by=admin_user.id,
|
||||
handled_at=datetime.utcnow(),
|
||||
handling_note="该员工已于11月15日离职,工资表中已处理",
|
||||
ai_suggestion="确认员工离职日期,如已办理社保减员可忽略此异常",
|
||||
)
|
||||
db.add(exc3)
|
||||
|
||||
# 12月异常(更多异常场景)
|
||||
task_12, year_12, month_12 = tasks[2]
|
||||
task_12.matched_count = 22
|
||||
task_12.exception_count = 3
|
||||
|
||||
# 异常4:重复员工
|
||||
exc4 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_12.id,
|
||||
exception_type=ExceptionType.DUPLICATE_EMPLOYEE.value,
|
||||
severity=ExceptionSeverity.CRITICAL.value,
|
||||
status=ExceptionStatus.PENDING.value,
|
||||
employee_id="EMP019",
|
||||
employee_name="韩冰冰",
|
||||
description="员工【韩冰冰】在工资表中出现重复记录,请核实",
|
||||
detail={"count": 2, "amounts": [8250.00, 8250.00]},
|
||||
salary_amount=16500.00,
|
||||
ai_suggestion="请检查是否是系统重复录入或是实际有两笔工资发放",
|
||||
)
|
||||
db.add(exc4)
|
||||
|
||||
# 异常5:金额为零
|
||||
exc5 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_12.id,
|
||||
exception_type=ExceptionType.ZERO_AMOUNT.value,
|
||||
severity=ExceptionSeverity.MEDIUM.value,
|
||||
status=ExceptionStatus.PENDING.value,
|
||||
employee_id="EMP022",
|
||||
employee_name="邓小丽",
|
||||
description="员工【邓小丽】本月实发工资为零,可能存在异常",
|
||||
detail={"expected_amount": 6000.00, "actual_amount": 0},
|
||||
salary_amount=0,
|
||||
ai_suggestion="请确认该员工是否已离职、停薪留职或工资发放被扣押",
|
||||
)
|
||||
db.add(exc5)
|
||||
|
||||
# 异常6:金额异常(过高)
|
||||
salary_3 = calculate_salary(EMPLOYEES[2], month_12, year_12)
|
||||
exc6 = ExceptionItem(
|
||||
company_id=company.id,
|
||||
task_id=task_12.id,
|
||||
exception_type=ExceptionType.UNUSUAL_AMOUNT.value,
|
||||
severity=ExceptionSeverity.HIGH.value,
|
||||
status=ExceptionStatus.PENDING.value,
|
||||
employee_id=EMPLOYEES[2]["id"],
|
||||
employee_name=EMPLOYEES[2]["name"],
|
||||
description=f"员工【{EMPLOYEES[2]['name']}】本月工资较上月有较大波动",
|
||||
detail={
|
||||
"current": salary_3["net_salary"],
|
||||
"previous_avg": 42000.00,
|
||||
"change_ratio": 0.25,
|
||||
},
|
||||
salary_amount=salary_3["net_salary"],
|
||||
difference_amount=8500.00,
|
||||
ai_suggestion="该员工工资增长超过20%,请确认是否有大额奖金或提成发放",
|
||||
)
|
||||
db.add(exc6)
|
||||
|
||||
# 5. 为每个月创建上传文件
|
||||
print("创建上传文件...")
|
||||
files = []
|
||||
for task, year, month in tasks:
|
||||
# 工资表
|
||||
salary_file = UploadedFile(
|
||||
company_id=company.id,
|
||||
task_id=task.id,
|
||||
file_type=FileType.SALARY.value,
|
||||
original_filename=f"{year}{month:02d}月工资表.xlsx",
|
||||
stored_filename=f"salary_{task.id}_{year}{month:02d}.xlsx",
|
||||
file_size=102400,
|
||||
mime_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
parse_status=ParseStatus.SUCCESS.value,
|
||||
)
|
||||
db.add(salary_file)
|
||||
|
||||
# 社保表
|
||||
social_file = UploadedFile(
|
||||
company_id=company.id,
|
||||
task_id=task.id,
|
||||
file_type=FileType.SOCIAL_SECURITY.value,
|
||||
original_filename=f"{year}{month:02d}月社保表.xlsx",
|
||||
stored_filename=f"social_{task.id}_{year}{month:02d}.xlsx",
|
||||
file_size=92160,
|
||||
mime_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
parse_status=ParseStatus.SUCCESS.value,
|
||||
)
|
||||
db.add(social_file)
|
||||
|
||||
# 个税表
|
||||
tax_file = UploadedFile(
|
||||
company_id=company.id,
|
||||
task_id=task.id,
|
||||
file_type=FileType.TAX.value,
|
||||
original_filename=f"{year}{month:02d}月个税表.xlsx",
|
||||
stored_filename=f"tax_{task.id}_{year}{month:02d}.xlsx",
|
||||
file_size=81920,
|
||||
mime_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
parse_status=ParseStatus.SUCCESS.value,
|
||||
)
|
||||
db.add(tax_file)
|
||||
|
||||
await db.flush()
|
||||
files.append((salary_file.id, social_file.id, tax_file.id))
|
||||
|
||||
# 6. 创建字段映射规则(关联到第一个文件作为示例)
|
||||
print("创建字段映射规则...")
|
||||
first_file_id = files[0][0] # 使用10月工资表的文件ID
|
||||
|
||||
field_mappings = [
|
||||
("员工编号", "employee_id", 0.98),
|
||||
("工号", "employee_id", 0.95),
|
||||
("姓名", "employee_name", 0.99),
|
||||
("员工姓名", "employee_name", 0.99),
|
||||
("部门", "department", 0.97),
|
||||
("所属部门", "department", 0.95),
|
||||
("岗位", "position", 0.90),
|
||||
("职位", "position", 0.90),
|
||||
("基本工资", "base_salary", 0.98),
|
||||
("岗位工资", "base_salary", 0.92),
|
||||
("应发工资", "gross_salary", 0.96),
|
||||
("实发工资", "net_salary", 0.97),
|
||||
("税前工资", "gross_salary", 0.93),
|
||||
("养老保险", "pension", 0.95),
|
||||
("医疗保险", "medical", 0.95),
|
||||
("社保", "social_security", 0.90),
|
||||
("公积金", "housing_fund", 0.95),
|
||||
("个税", "personal_income_tax", 0.98),
|
||||
("个人所得税", "personal_income_tax", 0.98),
|
||||
]
|
||||
|
||||
for source, standard, confidence in field_mappings:
|
||||
mapping = FieldMapping(
|
||||
company_id=company.id,
|
||||
file_id=first_file_id,
|
||||
source_field=source,
|
||||
standard_field=standard,
|
||||
confidence=confidence,
|
||||
confirmed=True,
|
||||
confirmed_by=admin_user.id,
|
||||
confirmed_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(mapping)
|
||||
|
||||
await db.commit()
|
||||
print("示例数据创建成功!")
|
||||
print("\n" + "="*50)
|
||||
print("登录信息:")
|
||||
print("="*50)
|
||||
for user_data in USERS_DATA:
|
||||
print(f" 邮箱: {user_data['email']}")
|
||||
print(f" 密码: {user_data['password']}")
|
||||
print(f" 角色: {user_data['role']}")
|
||||
print("-"*30)
|
||||
print("\n包含 3 个月(4月、5月、6月)的对账数据")
|
||||
print("共 25 名员工,多种异常场景")
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
print(f"创建示例数据失败: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
asyncio.run(init_sample_data())
|
||||
Reference in New Issue
Block a user