test_attendance_template_import.py 6.14 KB
#!/usr/bin/env python3
"""考勤汇总:导出模板 → 修改 Excel → 导入 → 校验列表与抽样数据。"""
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from urllib.parse import urlencode

import openpyxl

BASE = os.environ.get("API_BASE", "http://localhost:2015")
YEAR = int(os.environ.get("TEST_YEAR", "2026"))
MONTH = int(os.environ.get("TEST_MONTH", "5"))
SAMPLE_SIZE = int(os.environ.get("TEST_SAMPLE", "5"))


def curl_json(method, url, token=None, form_file=None, data=None):
    cmd = ["curl", "-s", "-X", method, url]
    if token:
        cmd += ["-H", f"Authorization: {token}"]
    if form_file:
        cmd += ["-F", f"file=@{form_file}"]
    if data and method == "POST":
        cmd += ["-H", "Content-Type: application/x-www-form-urlencoded", "-d", data]
    out = subprocess.check_output(cmd, text=True)
    return json.loads(out)


def login():
    body = curl_json(
        "POST",
        f"{BASE}/api/oauth/Login",
        data="account=admin&password=e10adc3949ba59abbe56e057f20f883e",
    )
    if body.get("code") != 200:
        raise RuntimeError(f"登录失败: {body}")
    return body["data"]["token"]


def get_template_users(token):
    qs = urlencode({"year": YEAR, "month": MONTH})
    body = curl_json("GET", f"{BASE}/api/Extend/lqattendancesummary/ImportTemplateUsers?{qs}", token=token)
    if body.get("code") != 200:
        raise RuntimeError(f"模板人员接口失败: {body}")
    data = body.get("data") or {}
    return data.get("list") or [], data.get("total", 0)


def build_excel(users, path):
    headers = [
        "员工ID", "员工姓名", "员工电话", "当月离职", "年份", "月份",
        "出勤天数(算薪在店天数)", "请假天数", "休息天数", "备注",
    ]
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "导入数据"
    ws.append(headers)
    for u in users:
        ws.append([
            u.get("id"), u.get("realName") or "无", u.get("mobilePhone") or "",
            "是" if u.get("leftInMonth") else "否", YEAR, MONTH, "", "", "", "",
        ])
    wb.save(path)


def modify_excel(path):
    wb = openpyxl.load_workbook(path)
    ws = wb["导入数据"]
    header = [c.value for c in ws[1]]
    idx = {h: i for i, h in enumerate(header)}
    work_col = idx["出勤天数(算薪在店天数)"]
    leave_col = idx["请假天数"]
    rest_col = idx["休息天数"]
    remark_col = idx["备注"]
    left_col = idx["当月离职"]
    modified = []
    for row_i in range(2, ws.max_row + 1):
        row = [ws.cell(row_i, c + 1).value for c in range(len(header))]
        user_id = row[idx["员工ID"]]
        name = row[idx["员工姓名"]]
        left = row[left_col] == "是"
        seq = row_i - 2
        if seq < SAMPLE_SIZE or left:
            work = 18.5 if not left else 12.0
            leave = 1.0 if seq % 2 == 0 else 0.5
            rest = 2.0
            ws.cell(row_i, work_col + 1, work)
            ws.cell(row_i, leave_col + 1, leave)
            ws.cell(row_i, rest_col + 1, rest)
            ws.cell(row_i, remark_col + 1, "自动化测试导入")
            modified.append({
                "userId": user_id, "name": name, "leftInMonth": left,
                "workDays": work, "leaveDays": leave, "restDays": rest,
            })
    wb.save(path)
    return modified


def import_excel(token, path):
    return curl_json(
        "POST",
        f"{BASE}/api/Extend/lqattendancesummary/ImportAttendanceDataFromExcel",
        token=token,
        form_file=path,
    )


def get_list(token):
    qs = urlencode({"year": YEAR, "month": MONTH, "currentPage": 1, "pageSize": 500})
    body = curl_json("GET", f"{BASE}/api/Extend/lqattendancesummary?{qs}", token=token)
    if body.get("code") != 200:
        raise RuntimeError(f"列表接口失败: {body}")
    rows = (body.get("data") or {}).get("list") or []
    return {x.get("userId"): x for x in rows}, len(rows)


def main():
    print(f"=== 考勤模板导入测试 {YEAR}-{MONTH:02d} ===")
    token = login()
    users, total = get_template_users(token)
    print(f"模板人员: {total} 人")
    left_count = sum(1 for u in users if u.get("leftInMonth"))
    print(f"其中当月离职: {left_count} 人")
    if not users:
        print("FAIL: 模板无人员")
        return 1

    with tempfile.TemporaryDirectory() as td:
        xlsx = Path(td) / f"考勤统计导入模板_{YEAR}年{MONTH}月_测试.xlsx"
        build_excel(users, xlsx)
        modified = modify_excel(xlsx)
        print(f"已修改 {len(modified)} 行测试数据")

        imp = import_excel(token, str(xlsx))
        if imp.get("code") != 200:
            print("FAIL 导入:", json.dumps(imp, ensure_ascii=False)[:2000])
            return 1
        imp_data = (imp.get("data") or {}).get("data") or imp.get("data") or {}
        print(f"导入: 成功 {imp_data.get('successCount')} 失败 {imp_data.get('failCount')}")

        by_user, list_total = get_list(token)
        print(f"列表当月汇总: {list_total} 条")

        ok = fail = 0
        for m in modified:
            row = by_user.get(m["userId"])
            if not row:
                print(f"  FAIL 列表缺失: {m['name']}")
                fail += 1
                continue
            w = float(row.get("workDays") or 0)
            l = float(row.get("leaveDays") or 0)
            r = float(row.get("restDays") or 0)
            remark = row.get("remark") or ""
            if abs(w - m["workDays"]) < 0.01 and abs(l - m["leaveDays"]) < 0.01 and abs(r - m["restDays"]) < 0.01:
                print(f"  OK {m['name']} 出勤={w} 当月离职={m['leftInMonth']} 备注前缀={'[Excel导入]' in remark}")
                ok += 1
            else:
                print(f"  FAIL {m['name']} 期望={m['workDays']} 实际={w}")
                fail += 1

        print(json.dumps({
            "templateTotal": total, "leftInMonthCount": left_count,
            "modifiedRows": len(modified), "importSuccess": imp_data.get("successCount"),
            "listTotal": list_total, "verifyOk": ok, "verifyFail": fail,
        }, ensure_ascii=False))
        return 0 if fail == 0 and imp_data.get("failCount", 0) == 0 else 1


if __name__ == "__main__":
    sys.exit(main())