seed_attendance_punch_week.sh
7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/bin/zsh
set -euo pipefail
BASE_URL="${BASE_URL:-http://localhost:2015}"
ACCOUNT="${ACCOUNT:-admin}"
PASSWORD="${PASSWORD:-e10adc3949ba59abbe56e057f20f883e}"
START_DATE="${1:-2026-03-16}"
DAYS="${DAYS:-7}"
USER_COUNT="${USER_COUNT:-5}"
GROUP_ID="${GROUP_ID:-}"
export BASE_URL ACCOUNT PASSWORD START_DATE DAYS USER_COUNT GROUP_ID
python3 - <<'PY'
import json
import os
import subprocess
import sys
from datetime import datetime, timedelta
from urllib.parse import urlencode
base = os.environ['BASE_URL']
account = os.environ['ACCOUNT']
password = os.environ['PASSWORD']
start_date = os.environ['START_DATE']
days = int(os.environ['DAYS'])
user_count = int(os.environ['USER_COUNT'])
group_id = os.environ.get('GROUP_ID') or ''
def call_api(method, path, *, token=None, json_body=None, form_body=None):
cmd = ['curl', '-s', '-X', method, f'{base}{path}']
if token:
cmd += ['-H', f'Authorization: {token}']
if json_body is not None:
cmd += ['-H', 'Content-Type: application/json', '-d', json.dumps(json_body, ensure_ascii=False)]
if form_body is not None:
cmd += ['-H', 'Content-Type: application/x-www-form-urlencoded', '-d', urlencode(form_body)]
raw = subprocess.check_output(cmd, text=True)
try:
data = json.loads(raw)
except json.JSONDecodeError:
raise RuntimeError(f'接口返回非 JSON:{raw}')
if data.get('code') != 200:
raise RuntimeError(f'接口调用失败:{path}\n{json.dumps(data, ensure_ascii=False)}')
return data
print('===> 登录获取 token')
login_res = call_api('POST', '/api/oauth/Login', form_body={
'account': account,
'password': password,
})
token = (login_res.get('data') or {}).get('token')
if not token:
raise RuntimeError(f'登录失败:{json.dumps(login_res, ensure_ascii=False)}')
print('===> 获取考勤分组')
info_res = call_api('GET', '/api/Extend/LqAttendanceSetting/Info', token=token)
setting_data = info_res.get('data') or {}
groups = [g for g in (setting_data.get('groups') or []) if g.get('isEnabled') == 1]
if not groups:
raise RuntimeError('未找到启用中的考勤分组,请先在考勤设置中心保存分组')
if not group_id:
group_id = groups[0]['id']
group_name = next((g.get('groupName') for g in groups if g.get('id') == group_id), group_id)
print(f'使用考勤分组:{group_name}({group_id})')
def get_group_users():
res = call_api(
'GET',
f'/api/Extend/LqAttendanceSetting/GroupUsers?GroupId={group_id}&OnJobStatus=1¤tPage=1&pageSize=100',
token=token,
)
return (res.get('data') or {}).get('list') or []
selected_users = []
for item in get_group_users():
if not item.get('id'):
continue
selected_users.append({
'id': item['id'],
'name': item.get('realName') or item.get('account') or item['id'],
'storeId': item.get('storeId'),
})
if len(selected_users) >= user_count:
break
if len(selected_users) < user_count:
print('===> 当前分组人数不足,尝试通过用户接口补充绑定成员')
users_res = call_api('GET', '/api/permission/Users?currentPage=1&pageSize=200&enabledMark=1', token=token)
candidates = (users_res.get('data') or {}).get('list') or []
selected_ids = {u['id'] for u in selected_users}
for candidate in candidates:
if len(selected_users) >= user_count:
break
user_id = candidate.get('id')
if not user_id or user_id in selected_ids:
continue
user_info_res = call_api('GET', f'/api/permission/Users/{user_id}', token=token)
user_info = user_info_res.get('data') or {}
if not user_info.get('id'):
continue
user_info['attendanceGroupId'] = group_id
call_api('PUT', f'/api/permission/Users/{user_id}', token=token, json_body=user_info)
selected_users.append({
'id': user_id,
'name': user_info.get('realName') or user_info.get('account') or user_id,
'storeId': user_info.get('mdid'),
})
selected_ids.add(user_id)
print(f'已绑定考勤分组:{user_info.get("realName") or user_id}')
if len(selected_users) < user_count:
raise RuntimeError(f'可用员工不足,仅找到 {len(selected_users)} 人,无法生成 {user_count} 人测试数据')
selected_users = selected_users[:user_count]
print('选中的员工:')
for item in selected_users:
print(f' - {item["name"]} ({item["id"]})')
def build_address(name, mode):
if mode == 0:
return f'{name} 门店正常上班打卡'
if mode == 1:
return f'{name} 门店迟到测试打卡'
if mode == 2:
return f'{name} 门店早退测试打卡'
return f'{name} 外勤上门服务测试打卡'
def build_punch_payload(user, attendance_date, mode, is_in):
if mode == 0:
punch_in = '08:55:00'
punch_out = '19:05:00'
punch_type = 1
elif mode == 1:
punch_in = '09:02:00'
punch_out = '19:03:00'
punch_type = 1
elif mode == 2:
punch_in = '08:57:00'
punch_out = '18:40:00'
punch_type = 1
else:
punch_in = '09:01:00'
punch_out = '18:52:00'
punch_type = 2
clock_time = punch_in if is_in else punch_out
photo_suffix = 'IN' if is_in else 'OUT'
return {
'UserId': user['id'],
'PunchDirection': 1 if is_in else 2,
'PunchType': punch_type,
'PunchTime': f'{attendance_date} {clock_time}',
'Longitude': 104.065735,
'Latitude': 30.656149,
'Address': build_address(user['name'], mode),
'PhotoUrl': f'https://dummyimage.com/320x240/409eff/ffffff.png&text={user["name"]}-{attendance_date}-{photo_suffix}',
'Remark': '脚本生成测试打卡数据',
}
print('===> 开始生成打卡数据')
start = datetime.strptime(start_date, '%Y-%m-%d')
created = []
for user_index, user in enumerate(selected_users):
for offset in range(days):
attendance_date = (start + timedelta(days=offset)).strftime('%Y-%m-%d')
mode = (user_index + offset) % 4
in_payload = build_punch_payload(user, attendance_date, mode, True)
out_payload = build_punch_payload(user, attendance_date, mode, False)
call_api('POST', '/api/Extend/LqAttendanceRecord/Punch', token=token, json_body=in_payload)
call_api('POST', '/api/Extend/LqAttendanceRecord/Punch', token=token, json_body=out_payload)
created.append({
'userId': user['id'],
'name': user['name'],
'date': attendance_date,
'mode': mode,
})
print(f'已生成:{user["name"]} {attendance_date}')
month = start.strftime('%Y-%m')
verify_res = call_api(
'GET',
f'/api/Extend/LqAttendanceRecord/MonthReport?month={month}¤tPage=1&pageSize=100',
token=token,
)
verify_data = verify_res.get('data') or {}
verify_list = verify_data.get('list') or []
matched_names = {item.get('employeeName') for item in verify_list}
matched_users = [item for item in selected_users if item['name'] in matched_names]
print('===> 生成完成')
print(json.dumps({
'groupId': group_id,
'groupName': group_name,
'startDate': start_date,
'days': days,
'selectedUsers': selected_users,
'verifiedUserCount': len(matched_users),
'verifiedUserNames': [item['name'] for item in matched_users],
'createdCount': len(created),
}, ensure_ascii=False, indent=2))
PY