Archived
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#!/bin/bash
|
||||
# 一键重启开发服务 —— 起完**核实到 200 才算数**。
|
||||
#
|
||||
# ./start.sh 重启 api + web(默认)
|
||||
# ./start.sh api 只重启 api(改了后端用这个;前端有 HMR,一般不必重启)
|
||||
# ./start.sh web 只重启 web
|
||||
# ./start.sh status 只看现在什么状态,一个进程都不动
|
||||
# ./start.sh stop 停掉两个
|
||||
#
|
||||
# 为什么要有它 —— 手工重启踩过的坑,这里逐条堵上:
|
||||
# ① `lsof -ti:5173` 会把**浏览器那条已建立的连接**也算成端口占用者,
|
||||
# 按它 kill -9 就是杀用户的浏览器。所以一律 `-sTCP:LISTEN`,只认真属主。
|
||||
# ② 只杀监听进程,**tsup 监视器留了下来** —— 它还活着,一秒后就重拉一个占回同一个端口。
|
||||
# 所以顺进程链往上走,把本仓库的监视器与 pnpm 壳一起收;收完还要**再核端口是不是真空了**。
|
||||
# ③ 拿旧 `ps` 快照里的 PID 去 kill,或者按名字 pkill —— 两样都误伤过。这里只从端口现查。
|
||||
# ④ 起完不核实就说「好了」。实测有一次 macOS 没有 `setsid` 根本起不来,
|
||||
# 而失败发生在后台里,不核实根本发现不了。
|
||||
set -u
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
REPO=$(pwd)
|
||||
LOGDIR="$REPO/.dev-logs"
|
||||
mkdir -p "$LOGDIR"
|
||||
WHAT=${1:-both}
|
||||
|
||||
API_PORT=8787
|
||||
WEB_PORT=5173
|
||||
PG_NAME=jcai-pg
|
||||
|
||||
say() { printf '%s\n' "$*"; }
|
||||
|
||||
# 端口的**真属主** —— 只认 LISTEN。不加这个过滤会捞到浏览器(坑①)
|
||||
owner_of() { lsof -ti:"$1" -sTCP:LISTEN 2>/dev/null | head -1; }
|
||||
|
||||
# 顺着进程链往上收:监听进程本身必收,再往上每一级**确认是本仓库的 dev 壳**才收。
|
||||
# 链长这样:node dist/main.js ← node …/tsup/…/cli-default.js ← pnpm --filter @app/api dev
|
||||
# 只收监听那一个的话,tsup 一秒后就把端口抢回去了(坑②)。
|
||||
kill_chain() {
|
||||
local pid=$1 depth=0 parent cmd
|
||||
KILLED=''
|
||||
while [ -n "$pid" ] && [ "$pid" != '1' ] && [ "$pid" != "$$" ] && [ "$depth" -lt 4 ]; do
|
||||
parent=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
|
||||
if [ "$depth" -gt 0 ]; then
|
||||
cmd=$(ps -o command= -p "$pid" 2>/dev/null)
|
||||
case "$cmd" in
|
||||
*"$REPO"* | *'@app/'*) : ;; # 本仓库的监视器 / pnpm 壳,收
|
||||
*) break ;; # 到终端或别人家的进程了,就此打住
|
||||
esac
|
||||
fi
|
||||
kill -9 "$pid" 2>/dev/null && KILLED="$KILLED $pid"
|
||||
pid=$parent
|
||||
depth=$((depth + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# 停到端口**真的空出来**为止 —— 收完还有人占,就是还有监视器在抢,再收一轮。
|
||||
stop_service() {
|
||||
local label=$1 port=$2 round owner all=''
|
||||
for round in 1 2 3 4; do
|
||||
owner=$(owner_of "$port")
|
||||
if [ -z "$owner" ]; then
|
||||
if [ -z "$all" ]; then
|
||||
[ "$round" = '1' ] && say " $label 本来就没在跑"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
if [ -z "$(owner_of "$port")" ]; then
|
||||
say " $label 已停 (pid$all)"
|
||||
return 0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
kill_chain "$owner"
|
||||
all="$all$KILLED"
|
||||
sleep 1
|
||||
done
|
||||
say " ! $label :$port 还被占着,收了几轮都有人抢回来 —— 手工看看 lsof -i:$port"
|
||||
return 1
|
||||
}
|
||||
|
||||
start_one() {
|
||||
local name=$1 port=$2 label=$3
|
||||
if [ -n "$(owner_of "$port")" ]; then
|
||||
say " $label 已在跑,不动它"
|
||||
return 0
|
||||
fi
|
||||
#
|
||||
# `exec` 不能省 —— 它让子 shell **被 pnpm 顶替掉**,进程链上不留 bash。
|
||||
#
|
||||
# 少了它(三种写法都试过:`(cmd & disown)`、双重 fork、`disown` 提到顶层),
|
||||
# start.sh 自己会赖着不退出、一直攥着 stdout,于是 `./start.sh | tail` 这类管道用法
|
||||
# **永远等不到结尾**(实测挂满 100 秒;服务其实早起好了,人只看见光标在闪)。
|
||||
# 换成 `sleep` 当被起的命令就不复现 —— 是 pnpm 特有的,不是写法通病。
|
||||
# `</dev/null` 断掉它跟终端的输入;`disown` 摘出作业表,免得停服务时刷一行 Killed: 9。
|
||||
# (macOS 没有 setsid,别用 —— 试过,command not found,而失败发生在后台里,`||` 兜不住)
|
||||
( cd "$REPO" && exec nohup pnpm --filter "@app/$name" dev < /dev/null > "$LOGDIR/$name.log" 2>&1 ) &
|
||||
disown 2>/dev/null
|
||||
say " $label 启动中… (日志 .dev-logs/$name.log)"
|
||||
}
|
||||
|
||||
# 轮询到 200 才算就绪 —— 不核实的「好了」等于没说(坑④)
|
||||
wait_ok() {
|
||||
local url=$1 label=$2 i code
|
||||
for i in $(seq 1 45); do
|
||||
code=$(curl -s -o /dev/null -m 3 -w '%{http_code}' "$url" 2>/dev/null)
|
||||
if [ "$code" = '200' ]; then
|
||||
say " ✓ $label 就绪 (${i}s)"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
say " ✗ $label 没有应答 —— 看 .dev-logs/$label.log"
|
||||
return 1
|
||||
}
|
||||
|
||||
http_code() { curl -s -o /dev/null -m 3 -w '%{http_code}' "$1" 2>/dev/null; }
|
||||
|
||||
check_db() {
|
||||
if ! docker ps --filter "name=$PG_NAME" --filter status=running --format '{{.Names}}' 2>/dev/null | grep -q "$PG_NAME"; then
|
||||
say " ! 数据库 $PG_NAME 没在跑 —— 先 pnpm db:up"
|
||||
return 1
|
||||
fi
|
||||
say " ✓ 数据库 $PG_NAME 在跑"
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$WHAT" in
|
||||
status)
|
||||
say '当前状态:'
|
||||
say " api :$API_PORT $(http_code "http://127.0.0.1:$API_PORT/api/health")"
|
||||
say " web :$WEB_PORT $(http_code "http://127.0.0.1:$WEB_PORT/")"
|
||||
say " 反代 /api $(http_code "http://127.0.0.1:$WEB_PORT/api/health")"
|
||||
check_db
|
||||
exit 0
|
||||
;;
|
||||
stop)
|
||||
say '停止服务:'
|
||||
stop_service api "$API_PORT"
|
||||
stop_service web "$WEB_PORT"
|
||||
exit 0
|
||||
;;
|
||||
api) say '重启 api:'; stop_service api "$API_PORT" ;;
|
||||
web) say '重启 web:'; stop_service web "$WEB_PORT" ;;
|
||||
both) say '重启 api + web:'; stop_service api "$API_PORT"; stop_service web "$WEB_PORT" ;;
|
||||
*) say "不认识的参数:$WHAT(可用:both / api / web / status / stop)"; exit 2 ;;
|
||||
esac
|
||||
|
||||
check_db || say ' (数据库不在也照起,但一调接口就会报连不上)'
|
||||
|
||||
# 两个都过一遍:刚停掉的那个会起来,另一个在跑就不动它、碰巧是停的就顺手带起来。
|
||||
# 「重启 api」不该把一个半残的环境留给人 —— 反代不通,浏览器里看到的还是坏的。
|
||||
start_one api "$API_PORT" "api :$API_PORT"
|
||||
start_one web "$WEB_PORT" "web :$WEB_PORT"
|
||||
|
||||
RC=0
|
||||
wait_ok "http://127.0.0.1:$API_PORT/api/health" 'api' || RC=1
|
||||
wait_ok "http://127.0.0.1:$WEB_PORT/" 'web' || RC=1
|
||||
# 反代要单独核:api 活着而反代不通,人在浏览器里看到的还是坏的
|
||||
PROXY=$(http_code "http://127.0.0.1:$WEB_PORT/api/health")
|
||||
if [ "$PROXY" = '200' ]; then
|
||||
say ' ✓ 反代 /api → 200'
|
||||
else
|
||||
say " ✗ 反代 /api → ${PROXY:-连不上}"
|
||||
RC=1
|
||||
fi
|
||||
|
||||
say ''
|
||||
if [ "$RC" = '0' ]; then
|
||||
say " 都好了 → http://localhost:$WEB_PORT/"
|
||||
else
|
||||
say ' 有起不来的,日志在 .dev-logs/'
|
||||
fi
|
||||
exit $RC
|
||||
Reference in New Issue
Block a user