小程序一些功能更新

This commit is contained in:
李琦
2026-09-29 10:57:01 +08:00
parent 0cb70a7618
commit 3bb7fa8db3
96 changed files with 13890 additions and 213 deletions

View File

@@ -0,0 +1,149 @@
package logic
import (
"testing"
"tool-api/internal/consts"
)
// TestClassifyImportRows 覆盖判据:同号同名→update、同名异号→conflict、缺号/格式错→error、新号→new。
func TestClassifyImportRows(t *testing.T) {
existing := []existingEmployee{
{Id: 1, Name: "李四", Phone: "13900000002"},
{Id: 2, Name: "王五", Phone: "13700000003"},
}
rows := []importRow{
{Name: "张三", Phone: "13800000001"}, // new
{Name: "李四", Phone: "13900000002"}, // update(同手机号)
{Name: "王五", Phone: "18800000009"}, // conflict(同名不同号)
{Name: "赵六", Phone: ""}, // error(缺号)
{Name: "钱七", Phone: "abc123"}, // error(格式非法)
}
got := classifyImportRows(rows, existing)
want := []string{
consts.ImportTypeNew,
consts.ImportTypeUpdate,
consts.ImportTypeConflict,
consts.ImportTypeError,
consts.ImportTypeError,
}
if len(got) != len(want) {
t.Fatalf("len=%d want %d", len(got), len(want))
}
for i := range want {
if got[i].Type != want[i] {
t.Fatalf("row %d type=%s want %s", i, got[i].Type, want[i])
}
}
if got[1].MatchedEmployeeId != 1 {
t.Fatalf("update 应命中现有员工 id=1,got %d", got[1].MatchedEmployeeId)
}
if got[2].MatchedEmployeeId != 2 {
t.Fatalf("conflict 应命中同名员工 id=2,got %d", got[2].MatchedEmployeeId)
}
}
// TestClassifyImportRowsPhoneNormalized 手机号去空格后再判重。
func TestClassifyImportRowsPhoneNormalized(t *testing.T) {
existing := []existingEmployee{{Id: 7, Name: "李四", Phone: "13900000002"}}
got := classifyImportRows([]importRow{{Name: "李四", Phone: " 139 0000 0002 "}}, existing)
if got[0].Type != consts.ImportTypeUpdate || got[0].MatchedEmployeeId != 7 {
t.Fatalf("去空格后应命中同一人,got type=%s id=%d", got[0].Type, got[0].MatchedEmployeeId)
}
}
// TestPlanImportOpsUnresolvedConflict 未裁决冲突 → unresolved>0(确认接口据此返回 4008)。
func TestPlanImportOpsUnresolvedConflict(t *testing.T) {
rows := []classifiedRow{
{Idx: 0, Type: consts.ImportTypeConflict, Name: "王五", Phone: "18800000009", MatchedEmployeeId: 2},
{Idx: 1, Type: consts.ImportTypeNew, Name: "张三", Phone: "13800000001"},
{Idx: 2, Type: consts.ImportTypeError, Name: "赵六", Phone: ""},
{Idx: 3, Type: consts.ImportTypeUpdate, Name: "李四", Phone: "13900000002", MatchedEmployeeId: 1},
}
ops, unresolved := planImportOps(rows, nil)
if unresolved != 1 {
t.Fatalf("未裁决冲突计数=%d want 1", unresolved)
}
for _, op := range ops {
if op.RowIdx == 0 {
t.Fatalf("未裁决的冲突行不应产生操作,got %v", op)
}
}
}
// TestPlanImportOpsMergeCreateIgnore 覆盖三种裁决各自的结果。
func TestPlanImportOpsMergeCreateIgnore(t *testing.T) {
base := []classifiedRow{
{Idx: 0, Type: consts.ImportTypeConflict, Name: "王五", Phone: "18800000009", MatchedEmployeeId: 2},
}
// merge → update 现有员工(含更新手机号)
ops, unresolved := planImportOps(base, map[int]string{0: consts.ImportActionMerge})
if unresolved != 0 || len(ops) != 1 {
t.Fatalf("merge: unresolved=%d ops=%d", unresolved, len(ops))
}
if ops[0].Kind != "update" || ops[0].UpdateId != 2 || ops[0].Phone != "18800000009" {
t.Fatalf("merge 应为 update 更新 id=2 且手机号为新号,got %+v", ops[0])
}
// create → insert(手机号不同,不违反 uk_ent_phone)
ops, unresolved = planImportOps(base, map[int]string{0: consts.ImportActionCreate})
if unresolved != 0 || len(ops) != 1 || ops[0].Kind != "insert" {
t.Fatalf("create 应为 insert,got unresolved=%d ops=%+v", unresolved, ops)
}
// ignore → skip
ops, unresolved = planImportOps(base, map[int]string{0: consts.ImportActionIgnore})
if unresolved != 0 || len(ops) != 1 || ops[0].Kind != "skip" {
t.Fatalf("ignore 应为 skip,got unresolved=%d ops=%+v", unresolved, ops)
}
// 非法动作 → 视为未裁决
_, unresolved = planImportOps(base, map[int]string{0: "whatever"})
if unresolved != 1 {
t.Fatalf("非法裁决动作应按未裁决处理,got unresolved=%d", unresolved)
}
}
// TestReimportSameFileAllUpdate 覆盖判据④:重复导入同一文件 → 全部为 update(不重复新增)。
func TestReimportSameFileAllUpdate(t *testing.T) {
rows := []importRow{
{Name: "张三", Phone: "13800000001"},
{Name: "李四", Phone: "13900000002"},
}
// 首次导入:库中无人 → 全部 new
first := classifyImportRows(rows, nil)
for _, c := range first {
if c.Type != consts.ImportTypeNew {
t.Fatalf("首次导入应为 new,got %s", c.Type)
}
}
// 再次导入同一文件:库中已有这两人 → 全部 update
existing := []existingEmployee{
{Id: 1, Name: "张三", Phone: "13800000001"},
{Id: 2, Name: "李四", Phone: "13900000002"},
}
second := classifyImportRows(rows, existing)
for _, c := range second {
if c.Type != consts.ImportTypeUpdate {
t.Fatalf("重复导入应全部为 update,got %s(row %d)", c.Type, c.Idx)
}
}
}
// TestCountClassified 预览计数口径。
func TestCountClassified(t *testing.T) {
rows := []classifiedRow{
{Type: consts.ImportTypeNew},
{Type: consts.ImportTypeNew},
{Type: consts.ImportTypeUpdate},
{Type: consts.ImportTypeError},
{Type: consts.ImportTypeConflict},
}
c := countClassified(rows)
if c.inserted != 2 || c.updated != 1 || c.skipped != 1 || c.conflict != 1 {
t.Fatalf("counts=%+v want inserted2/updated1/skipped1/conflict1", c)
}
}