feat(账号): 账号台账(web 页 + 任务取号 + 设备端身份页);fix(剪贴板): 注入通道改走设备端 Agent
一、账号台账(新表 device_account,schema v8) - core/ledger.py:CRUD、Excel 粘贴解析(Tab 分隔 / 表头乱序缺列 / 是·否布尔 / 逐行留痕)、 按范围取号(本机台账 / 全部 / 按设备分组)、设备端账号块、dry_run 预览 - 新「账号」顶级 Tab(权限 devices):列表 + 搜索 + 设备筛选 + 增删改 + 粘贴导入(默认先预览再写,逐行显示 新增/覆盖/跳过/失败 + 留痕) - 设备池加「账号 N」列(0 个显示"未登记"),点开看该设备账号明细 - 任务「条件判断」新增 cmp_source / cmp_group:候选值可直接来自台账, 与手填值**合并(OR)**;取不到号时回落手填值,并把原因写进步骤明细与日志 (否则"永远走 else 分支"而任务照样显示成功,最难查) - 设备端契约:身份页多推 accounts_b64(base64 JSON;默认不含手机号; 超预算按整条丢且绝不算字节切),见 doc/DEVICE_AGENT.md §5.2.1 - 备份/文档红线:TABLE_LABELS 加「账号台账」;DATA_MODEL/API/ARCHITECTURE/DEPLOY/ TASK_DEV/DEVICE_AGENT/DEVELOPMENT/README 同步;顺手补上 DATA_MODEL 漏列的 done_mark ⚠ 台账里的抖音号是**纯号**,只当"比对用的候选值":绝不能拿去填「去重」的身份元素 (身份是元素原文逐字算 key,格式不同会让去重静默失效)。代码与文档都写明了。 二、剪贴板注入通道(修:平台还在调早期的独立 APK) - 改为按顺序尝试:设备端 Agent(com.example.deviceagent/.ClipActivity)→ 旧版独立 ClipInject 兜底;两个都没有时报"需要设备端 Agent"(不再只说 ClipInject) - 读回验证改成**轮询到 3 秒**:透明 Activity 要等窗口拿到焦点才写, 原来只睡 0.5s 会读到上一次的内容 → 误报"写入可能被拒"(实测内容已写入却回失败)
This commit is contained in:
+39
-4
@@ -19,7 +19,7 @@ var STEP_LIB=[
|
||||
{type:'wait_el',label:'等待元素',icon:'⏳',cat:'flow',params:{selector_type:'xpath',selector_value:'',timeout:10}},
|
||||
{type:'loop',label:'循环块',icon:'↻',cat:'flow',params:{loop_mode:'rounds',max_iterations:10,loop_duration:600,children:[]}},
|
||||
{type:'group',label:'动作组',icon:'📦',cat:'flow',params:{children:[]}},
|
||||
{type:'if_el',label:'条件判断',icon:'❓',cat:'flow',params:{selector_type:'xpath',selector_value:'',timeout:3,ocr_click:false,cmp_op:'',cmp_value:'',ident_type:'text',ident_value:'',then:[],else:[]}},
|
||||
{type:'if_el',label:'条件判断',icon:'❓',cat:'flow',params:{selector_type:'xpath',selector_value:'',timeout:3,ocr_click:false,cmp_op:'',cmp_value:'',cmp_source:'',cmp_group:'',ident_type:'text',ident_value:'',then:[],else:[]}},
|
||||
{type:'mark_done',label:'记为已做',icon:'✅',cat:'flow',params:{selector_type:'text',selector_value:''}},
|
||||
{type:'notify',label:'发通知',icon:'🔔',cat:'flow',params:{title:'',message:'',level:'info'}},
|
||||
{type:'stop_self',label:'停止本设备',icon:'⛔',cat:'flow',params:{reason:''}},
|
||||
@@ -464,16 +464,36 @@ var _stepEditor={
|
||||
var isDedup=(p.selector_type==='dedup');
|
||||
var dyn=(p.selector_type==='screen'||p.selector_type==='foreground'||isDedup);
|
||||
// 文本比对:屏幕状态没有文本可比(亮/熄)、去重也不比文本,只有元素/OCR/前台 App 才给这块
|
||||
// 候选值来源:默认"手填"(行为与以前完全一致);也可以直接吃「账号」页的台账
|
||||
var cmpSrc=p.cmp_source||'';
|
||||
var fromLedger=(cmpSrc==='device'||cmpSrc==='all'||cmpSrc==='group');
|
||||
var cmpRow=dyn?'':'<div class="form-row"><div class="form-group"><label>文本比对</label>'+
|
||||
'<select class="form-control" data-key="cmp_op">'+
|
||||
[['','不比对(只看元素在不在)'],['等于','等于'],['不等于','不等于'],['包含','包含'],['不包含','不包含']]
|
||||
.map(function(o){return '<option value="'+o[0]+'"'+((p.cmp_op||'')===o[0]?' selected':'')+'>'+o[1]+'</option>';}).join('')+
|
||||
'</select></div>'+
|
||||
'<div class="form-group" style="grid-column:span 2"><label>比对的值(可多行,任一命中即可)</label>'+
|
||||
'<div class="form-group"><label>候选值来源</label>'+
|
||||
'<select class="form-control" data-key="cmp_source" onchange="_stepEditor._cmpSourceChanged(this)">'+
|
||||
[['','手填(默认)'],['device','本机台账'],['all','全部台账'],['group','按设备分组']]
|
||||
.map(function(o){return '<option value="'+o[0]+'"'+((p.cmp_source||'')===o[0]?' selected':'')+'>'+o[1]+'</option>';}).join('')+
|
||||
'</select><div class="help">台账在「账号」页维护</div></div>'+
|
||||
(cmpSrc==='group'?
|
||||
'<div class="form-group"><label>设备分组名</label>'+
|
||||
'<input class="form-control" data-key="cmp_group" value="'+esc(p.cmp_group||'')+'" '+
|
||||
'placeholder="分组名(工具→设备分组)"></div>':'')+
|
||||
// 选台账时**只隐藏、不删**这个输入框:_syncParams 是按 DOM 存在与否收集的,
|
||||
// 删了就等于把用户手填的值悄悄抹掉(同类坑见本文件 927 行附近的注释)
|
||||
'<div class="form-group" style="grid-column:span 2'+(fromLedger?';display:none':'')+'">'+
|
||||
'<label>比对的值(可多行,任一命中即可)</label>'+
|
||||
'<textarea class="form-control" data-key="cmp_value" rows="2" style="font-family:var(--mono)" '+
|
||||
'placeholder="如 35377983067 35377983068">'+esc(p.cmp_value||'')+'</textarea>'+
|
||||
'<div class="help">一行一个(也认 <code>|</code> 分隔):<b>任意一个命中就算命中</b>——多个号的场景把号都列上。'+
|
||||
'<b>逗号不当分隔符</b>,因为要比对的文本本身常带逗号</div></div></div>';
|
||||
'<b>逗号不当分隔符</b>,因为要比对的文本本身常带逗号'
|
||||
+(fromLedger?'<br><b>候选值来自台账</b>:这台设备在台账里登记的抖音号会作为候选,'
|
||||
+'手填的值<b>追加</b>在后面(台账取不到号时只用手填值,不会把任务打哑)。'
|
||||
+'<br>⚠ 台账只提供<b>比对用的号</b>;<b>不要</b>把台账号填到「去重」的身份元素里'
|
||||
+'(身份是元素原文、逐字比,格式不同会让去重静默失效)。':'')
|
||||
+'</div></div></div>';
|
||||
// 「去重」条件:身份元素用 ident_type/ident_value(主选择器那行只剩类型下拉)
|
||||
var identRow=isDedup?_selRowHtml(p,
|
||||
'<div class="help"><b>身份元素</b>:填"账号"那个元素(点「抓取元素」选,如抖音号那个 TextView)。'
|
||||
@@ -814,6 +834,13 @@ var _stepEditor={
|
||||
g.style.display=g.dataset.lmode===mode?'block':'none';
|
||||
});
|
||||
},
|
||||
// 条件判断:候选值来源切到台账时,把"比对的值"藏起来(按分组还要显示分组名输入框)
|
||||
_cmpSourceChanged:function(sel){
|
||||
// 同 _condTypeChanged:先把 DOM 的值同步回 _steps,否则重渲染会吞掉刚改的东西
|
||||
this._syncFromDom();
|
||||
var self=this;
|
||||
setTimeout(function(){self._renderCanvas();},0);
|
||||
},
|
||||
// 条件判断:选择器类型切到 OCR 时显示"命中后点击"开关
|
||||
_condTypeChanged:function(sel){
|
||||
// 条件类型一变,"值"控件也要跟着换(元素值输入框 / 屏幕亮熄下拉 / 包名输入),
|
||||
@@ -930,10 +957,18 @@ var _stepEditor={
|
||||
if(s.params.selector_type!=='dedup'&&!s.params.selector_value){
|
||||
warnings.push(name+' "'+label+'": 条件判断的选择器为空,会直接走"未找到"分支。');
|
||||
}
|
||||
if(cmpUsed&&s.params.cmp_op&&!String(s.params.cmp_value||'').trim()){
|
||||
var cmpSrcV=(s.params.cmp_source||'');
|
||||
var fromLedger=(cmpSrcV==='device'||cmpSrcV==='all'||cmpSrcV==='group');
|
||||
if(cmpUsed&&s.params.cmp_op&&!String(s.params.cmp_value||'').trim()&&!fromLedger){
|
||||
// 候选值来自台账时允许手填留空(台账取号就够)→ 不报这条
|
||||
warnings.push(name+' "'+label+'": 选了"文本比对('+s.params.cmp_op+')"但没填比对的值,'
|
||||
+'执行时永远不命中(相当于每次都走"未找到时"分支)。');
|
||||
}
|
||||
if(cmpUsed&&fromLedger&&s.params.cmp_op){
|
||||
warnings.push(name+' "'+label+'": 候选值来自台账 —— 台账里没有这台设备'
|
||||
+'(或设备池里的设备名与台账「设备号」对不上)时会取不到号,'
|
||||
+'这一步将永远走"未找到"分支。请到「账号」页核对设备号。');
|
||||
}
|
||||
if(s.params.selector_type==='dedup'){
|
||||
hasDedupCheck=true;
|
||||
// 空值也收进来:一边填了、另一边没填 = 两处身份不一致(去重会对不上),
|
||||
|
||||
Reference in New Issue
Block a user