用 Claude Code Vibe Coding 从 0 到 1 开发过程

用 Claude Code Vibe Coding 从 0 到 1 开发过程

背景

需要完成一个传秤工具,
跟它差不多: blog.pospal.cn/kb/6827

开发流程

  1. 技术选型
  2. 需求分析与文档编写
  3. 细化为详细需求:减少负责人负担
  4. 创建项目骨架与登录。
    • 完成后,让claude写测试,测试没问题,再让他转为测试的skill
  5. 封装自定义 Skill
    • 根据项目情况,让claude改team的skill
  6. 启动团队,完成需求
    • 然后耗了4个claude pro的账户额度,完成了需求。
    • ui还需要继续优化下的。

权限:

  1. claude –dangerously-skip-permissions
  2. 后端项目挂载到本地,且开放数据库

技术选型:

  1. electron:
    1. 浪费,一个小小工具,还塞个浏览器。
    2. 客户端运行,基本在win7,只能使用旧的electron
  2. C# WinForms: 我没写过,连它变量怎么声明我都不知道。
    我还是选择了C# WinForms

需求分析

这部分是最为复杂! 删了又改,改了又删…
我写的需求文档,每一句都是经过我脑海验证:这样的运行流程,是可行的。
需求如下:

    ## 背景
    需要实现一个服务器将商品数据下发给秤的功能。
    端:服务器 -> 传秤工具 ->  条码秤
    "秤管理"已经实现了,表在:"docs\秤\数据设计.md"

    现在我要将所有环节联通。
    传秤工具我希望用来实现,因为要考虑win7。
    现在,现在本项目已经创建一个"传秤工具"的骨架,但是功能还没完成的,包括后端的!
    # 需求
    本项目用的C# WinForms,已经搭建的基础骨架。
    # 首页
    左侧有两个菜单:
    1. 秤管理
    2. 秤日志

    ## 秤管理页
    请求后端api,用表格展示列出所有秤。
    api需要你自己看后端查询,有这个api了。

    我们还需要额外一列:设备状态  
    这个需要ping那个设备,如果能成功 则是在线的,否则是离线的。

    ## 秤日志
    展示服务器下方给"秤工具",工具下发给"条码秤"的记录。

    # 更新逻辑
    后端需要创建一个websocket,使用swoole创建。

    有些坑的,比如数据库在laravel是单例,而在swoole协程共用会导致意想不到的bug。
    还有laravel的Request全局实例。
    为了避免这些坑,你要参考:app/Services/WebSocket/WebSocketService.php

    后端需要创建一个新的命令新的类来专门处理这个秤的websocket服务。
    静态变量:
    [
    fd => {
    'supermarket_id':xxx,
    'user_id':xxx
    },
    ]

    链接和关闭事件如何处理,我想不用我多说了,因为你可以参考刚刚那个websocket服务类。

    超市配置有个字段: 改价是否自动传秤。
    需要你完成后端的api:app/Http/Controllers/V2/ScaleController.php的sync,将秤的商品发送给秤工具。


    具体流程这样的:
    前端在web点击"同步到秤" 
    后端api查询这个秤的所有商品,通过http发送到onRequest事件 (只需要发送商品id列表) 
    -> onRrequest查询所有商品,发送给秤工具,然后onRequest返回已下发同步商品 -> web显示消息

    至于改价: // 这个需要要实现的,现在还没做的。
    在模型事件处理,判断价格是否有改变。
    如果改价,再判断是否开启。 - 超市表那个是否改价字段,因为是热数据,要加入到redis缓存  放到:app/Services/CacheGetConfig.php,对了 别忘"秤设置",如果修改了"是否改价"字段,需要重载缓存的。
    如果开启,创建一个队列,队列来进行发送http给秤。

    # 秤工具和后端websocket的补充
    补充1:
    秤工具在登录成功后,链接后端websocket。

    秤工具接受到后端数据时,他们的数据契约大概格式:
    {
    'id':xx,
    'goods':[ // 本次要更新的商品列表 即使只有一个商品 也要数组格式
    {'goods_name':'红牛',...},
    ...
    ]
    }
    为防止竞态下发给秤,秤工具需要实现队列下发,也就是将后端给的数据,转为队列任务。
    在登录成功后,启动队列执行。

    队列下发后,需要将情况加入到日志,要非常的详细的。
    可以看到开始执行事件  结束时间 下发商品列表 下发情况等。
    队列不需要重试,失败就失败了,有日志就行。

    ***
    补充2:
    web端需要可以看到秤是否在线,因此服务器在onOpen和onClose需要修改秤表的是否在线字段。
    这个字段我不知道有没有,没有你就自己加。

    # 秤工具下发给秤的说明
    下发示例参考:"docs\秤\test_dahua_155.py",这是已经经过测试的了。

    # 验收测试
    ## 后端
    我们重点是关注后端websocket服务的。
    websocket客户端:https://wiki.swoole.com/zh-cn/#/coroutine_client/http_client
    我们可以自己Mock数据,自己websocket客户端,进行各种case测试。

    ## 前端
    可以用.claude\skills\test-barcode-scale\SKILL.md这个skill。
    已经有一把秤了的,就是数据库那一条数据。
    192.168.xx.xxx这个。

    还有一个,如果后端更新的秤,比如改了秤的ip或者端口之类的。
    要通知秤工具刷新。
    因此:在后端的新增或者修改秤api,需要增加一个http请求到websocket的onRrequest。
    ...
    最后通知秤工具 秤工具来重新请求秤列表。

转为详细需求

/refine-task  docs\需求.md   最后写入到docs/详细需求.md

创建登录页和骨架

/team 我需要完成项目骨架。
docs\xx-web\docs\design\login-login.md是web的登录,参考他。
完成本项目的登录功能。
你的目的不仅仅为了完成这个登录。
还需要完成项目架构。
比如全局环境 接口请求基类 公共函数等

实现需求

/team docs/详细需求.md

我的claude code的vibe coding从0到1构建过程

用 Claude Code Vibe Coding 从 0 到 1 开发过程

用 Claude Code Vibe Coding 从 0 到 1 开发过程

Skill

我创建了两个项目级别 skill 和一个全局skill帮我完成工作。
当然都是claude写的。

测试的skill 给team使用

我先让claude写个单元测试和ui测试,然后让他总结成 skill

---
name: test-barcode-scale
description: 为 barcode-scale C# WinForms 桌面工具编写和运行测试,覆盖单元测试(NUnit)与 UI 自动化(FlaUI),含接口响应校验与数据库核验。
---

# barcode-scale 测试指南

## 项目信息

- **桌面项目**`C:\Users\xxx\code\barcode_scale\ScaleTool\`
- **测试项目**:`C:\Users\xxx\code\barcode_scale\ScaleTool.Tests\`(net462,SDK 风格 csproj)
- **框架**:C# .NET 4.0 WinForms(主项目)/ .NET 4.6.2(测试项目)
- **单元测试**:NUnit 3
- **UI 自动化**:FlaUI(FlaUI.UIA3 + FlaUI.Core)—— 无需安装额外服务,直接调用 Windows UI Automation
- **后端代码**:`C:\Users\xxx\code\api-xxx`(读懂接口逻辑)

## 运行测试

```powershell
cd C:\Users\xxx\code\barcode_scale\ScaleTool.Tests

# 全部测试
dotnet test

# 只跑单元测试
dotnet test --filter "FullyQualifiedName~ScaleTool.Tests" --filter "FullyQualifiedName!~UI"

# 只跑 UI 测试
dotnet test --filter "FullyQualifiedName~UI"

# 跑指定类
dotnet test --filter "FullyQualifiedName~LoginFormUITests"
```

## ⚠️ 常见坑(必读)

### 1. Designer.cs 必须显式设 Name
FlaUI 通过 `AutomationId` 定位控件,WinForms 的 `AutomationId` 来自控件的 `Name` 属性。
**手写的 Designer.cs 不会自动设 Name**,必须手动加,否则 `FindFirstDescendant` 返回 null。

```csharp
// LoginForm.Designer.cs — 每个需要在测试中定位的控件都要加
this.txtServer.Name   = "txtServer";
this.txtUsername.Name = "txtUsername";
this.txtPassword.Name = "txtPassword";
this.btnLogin.Name    = "btnLogin";
this.lblError.Name    = "lblError";
```

### 2. 主项目只支持 C# 5
主项目目标 .NET 4.0,默认 C# 语言版本为 5,以下语法会编译报错:
```csharp
// ❌ C# 7 才支持
if (!Validate(a, b, out var err)) { }

// ✅ 兼容写法
string err;
if (!Validate(a, b, out err)) { }
```
测试项目(net462)不受此限制,可用现代语法。

### 3. FlaUI 按钮点击是异步的——点击后不能立即读结果
`Click()` 底层用 `InvokePattern.Invoke()`,**返回时 click handler 还没执行完**。
紧接着 `FindFirstDescendant("lblError")` 会得到 null(控件还是 `Visible=false`)。

正确做法:轮询等待控件变为可见:

```csharp
private Label WaitForLabel(string automationId, int timeoutMs = 2000)
{
    var deadline = DateTime.Now + TimeSpan.FromMilliseconds(timeoutMs);
    while (DateTime.Now < deadline)
    {
        var el = _win.FindFirstDescendant(cf => cf.ByAutomationId(automationId));
        if (el != null && !el.IsOffscreen)
            return el.AsLabel();
        System.Threading.Thread.Sleep(50);
    }
    return null;
}
```

### 4. `Visible=false` 的控件在 UIA 树里不存在
`FindFirstDescendant` 对 `Visible=false` 的控件返回 **null**,`IsOffscreen=true` 也视同不可见。
必须用上面的 WaitForLabel 轮询,而不是直接读 `.Text`。

### 5. 多 fixture 之间必须隔离 config 文件
如果 app 把 token 等状态写入本地 config(如 `%AppData%\ScaleTool\config.json`),
**一个 fixture 登录成功后会留下 token**,下一个 fixture 启动 app 时会直接跳到 MainForm,
导致 LoginForm 测试看不到登录界面。

每个 UI fixture 的 SetUp/TearDown 都要备份→删除→恢复:

```csharp
private static readonly string ConfigPath = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
    "ScaleTool", "config.json");
private static readonly string BackupPath = ConfigPath + ".bak_ui";

[SetUp]
public void SetUp()
{
    // 先杀残留进程
    foreach (var p in System.Diagnostics.Process.GetProcessesByName("ScaleTool"))
        try { p.Kill(); p.WaitForExit(1000); } catch { }
    System.Threading.Thread.Sleep(200);

    // 清空 config,确保 app 显示 LoginForm
    if (File.Exists(ConfigPath)) File.Copy(ConfigPath, BackupPath, overwrite: true);
    if (File.Exists(ConfigPath)) File.Delete(ConfigPath);

    _automation = new UIA3Automation();
    _app = Application.Launch(APP_PATH);
    _win = _app.GetMainWindow(_automation, TimeSpan.FromSeconds(5));
}

[TearDown]
public void TearDown()
{
    try { _app?.Kill(); _app?.Dispose(); } catch { }
    try { _automation?.Dispose(); } catch { }
    // 恢复 config
    try
    {
        if (File.Exists(ConfigPath)) File.Delete(ConfigPath);
        if (File.Exists(BackupPath)) File.Move(BackupPath, ConfigPath);
    }
    catch { }
    System.Threading.Thread.Sleep(300);
}
```

### 6. 窗口标题用精确匹配,不要用 Contains
`WaitForWindow` 用 `.Contains()` 时,`"传秤工具".Contains("传秤工具")` 会同时匹配
`"传秤工具 - 登录"`(LoginForm)和 `"传秤工具"`(MainForm),导致集成测试提前返回错误窗口。

```csharp
// ❌ 会误匹配子标题
var win = wins.FirstOrDefault(w => w.Title.Contains("传秤工具"));

// ✅ 精确匹配
var win = wins.FirstOrDefault(w => w.Title == "传秤工具");
```

### 7. PasswordChar 字段必须用键盘输入
有 `PasswordChar` 的 TextBox,UIA 的 `SetValue`(即 `box.Text = ...`)**无效或被忽略**。
必须先 Focus 再用键盘模拟输入:

```csharp
using FlaUI.Core.Input;
using FlaUI.Core.WindowsAPI;

private void TypeInto(Window win, string automationId, string text)
{
    var el = win.FindFirstDescendant(cf => cf.ByAutomationId(automationId));
    el.Focus();
    Keyboard.TypeSimultaneously(VirtualKeyShort.CONTROL, VirtualKeyShort.KEY_A); // 全选清空
    Keyboard.Type(text);
    System.Threading.Thread.Sleep(100);
}
```

### 8. UI 测试失败后 exe 进程残留
UI 测试中途失败时,`TearDown` 的 `_app.Kill()` 可能未执行,导致 exe 进程残留。
下次编译会报"文件被占用",先手动清理:
```powershell
Get-Process -Name "ScaleTool" -ErrorAction SilentlyContinue | Stop-Process -Force
```

### 9. 重编译主项目 exe
测试项目只引用 `LoginValidator.cs` 等纯逻辑文件,**不会自动重编译主项目 exe**。
修改了 Form 代码后,需要手动重建 exe:
```powershell
& "C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" `
  "C:\Users\xxx\code\barcode_scale\ScaleTool\ScaleTool.csproj" `
  /p:Configuration=Debug /t:Build /v:minimal
```
新文件加入主项目后,还要同步加进旧式 `.csproj` 的 `<ItemGroup>` 里:
```xml
<Compile Include="LoginValidator.cs" />
```

---

## 测试文件结构模板

### 单元测试(NUnit)

业务逻辑必须提取到独立的无 WinForms 依赖的类(如 `LoginValidator.cs`),才能单元测试。

```csharp
using NUnit.Framework;
using ScaleTool;

namespace ScaleTool.Tests
{
    [TestFixture]
    public class LoginValidatorTests
    {
        [Test] public void AllFieldsFilled_ReturnsTrue() { }
        [Test] public void EmptyServer_ReturnsFalse() { }
        [Test] public void EmptyUsername_ReturnsFalse() { }
        [Test] public void EmptyPassword_ReturnsFalse() { }
        [Test] public void WhitespaceOnly_ReturnsFalse() { }
    }
}
```

### UI 自动化测试(FlaUI + NUnit)

```csharp
using System;
using System.IO;
using System.Threading;
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Input;
using FlaUI.Core.WindowsAPI;
using FlaUI.UIA3;
using NUnit.Framework;

namespace ScaleTool.Tests.UI
{
    [TestFixture]
    [Apartment(System.Threading.ApartmentState.STA)]  // FlaUI 必须 STA
    public class LoginFormUITests
    {
        private const string APP_PATH =
            @"C:\Users\xxx\code\barcode_scale\ScaleTool\bin\Debug\ScaleTool.exe";

        // 如果 app 有本地 config,必须在每个 fixture 隔离(见坑 5)
        private static readonly string ConfigPath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "ScaleTool", "config.json");
        private static readonly string BackupPath = ConfigPath + ".bak_ui";

        private Application _app;
        private UIA3Automation _automation;
        private Window _win;

        [SetUp]
        public void SetUp()
        {
            // 清理残留进程(见坑 8)
            foreach (var p in System.Diagnostics.Process.GetProcessesByName("ScaleTool"))
                try { p.Kill(); p.WaitForExit(1000); } catch { }
            Thread.Sleep(200);

            // 清空 config,确保 app 显示 LoginForm(见坑 5)
            if (File.Exists(ConfigPath)) File.Copy(ConfigPath, BackupPath, overwrite: true);
            if (File.Exists(ConfigPath)) File.Delete(ConfigPath);

            _automation = new UIA3Automation();
            _app = Application.Launch(APP_PATH);
            _win = _app.GetMainWindow(_automation, TimeSpan.FromSeconds(5));
        }

        [TearDown]
        public void TearDown()
        {
            try { _app?.Kill(); _app?.Dispose(); } catch { }
            try { _automation?.Dispose(); } catch { }
            // 恢复 config(见坑 5)
            try
            {
                if (File.Exists(ConfigPath)) File.Delete(ConfigPath);
                if (File.Exists(BackupPath)) File.Move(BackupPath, ConfigPath);
            }
            catch { }
            Thread.Sleep(300);
        }

        [Test] public void EmptyFields_ClickLogin_ShowsError() { }
        [Test] public void ServerOnly_ClickLogin_ShowsUsernameError() { }

        // helpers

        private void Click(string automationId) =>
            _win.FindFirstDescendant(cf => cf.ByAutomationId(automationId)).AsButton().Click();

        // 必须轮询等待,不能点击后立即读(见坑 3、4)
        private Label WaitForLabel(string automationId, int timeoutMs = 2000)
        {
            var deadline = DateTime.Now + TimeSpan.FromMilliseconds(timeoutMs);
            while (DateTime.Now < deadline)
            {
                var el = _win.FindFirstDescendant(cf => cf.ByAutomationId(automationId));
                if (el != null && !el.IsOffscreen) return el.AsLabel();
                Thread.Sleep(50);
            }
            return null;
        }

        // 有 PasswordChar 的字段必须用键盘(见坑 7)
        private void TypeInto(string automationId, string text)
        {
            var el = _win.FindFirstDescendant(cf => cf.ByAutomationId(automationId));
            el.Focus();
            Keyboard.TypeSimultaneously(VirtualKeyShort.CONTROL, VirtualKeyShort.KEY_A);
            Keyboard.Type(text);
            Thread.Sleep(100);
        }
    }
}
```

## FlaUI 常用元素定位

```csharp
// 按 AutomationId(= 控件 Name 属性,需在 Designer.cs 显式设置)
var txt = _win.FindFirstDescendant(cf => cf.ByAutomationId("txtUsername")).AsTextBox();

// 按窗口标题文字
var btn = _win.FindFirstDescendant(cf => cf.ByName("登  录")).AsButton();

// 输入文本(先清空再输入)
txt.Text = "";
txt.Enter("admin");

// 点击
_win.FindFirstDescendant(cf => cf.ByAutomationId("btnLogin")).AsButton().Click();

// 断言 Label 文字
var lbl = _win.FindFirstDescendant(cf => cf.ByAutomationId("lblError")).AsLabel();
Assert.AreEqual("请填写用户名", lbl.Text);

// 断言控件可见(IsOffscreen=false 表示可见)
Assert.IsFalse(lbl.IsOffscreen);
```

## 写测试前的必做步骤(禁止跳过)

**第一步:读代码,枚举所有分支**

先阅读对应 Form 和业务类,列出:

1. **所有 if/else 分支**:每个分支对应一个 case
2. **所有表单字段校验**:必填、格式要求
3. **所有 API 调用**:请求参数是否正确
4. **所有异常路径**:网络超时、服务器错误码

将枚举出的分支列成注释放在测试文件顶部,再逐一编写 case。

## 多 Case 覆盖策略

| Case 类型 | 说明 |
|-----------|------|
| 正常流程 | 合法输入,断言返回成功 + **DB 有记录** |
| 必填项校验 | 空字段提交,断言错误提示、窗口未关闭 |
| 格式校验 | 非法格式(IP、端口),断言提示 |
| 边界值 | 超长字段、空字符串、纯空格 |
| 状态切换 | 启用/禁用秤,断言接口响应 + **DB 状态字段** |
| 同步 | 推送商品到秤,断言接口调用参数正确 |
| 网络失败 | Mock 接口超时/500,断言界面显示错误提示 |

## 数据库核验

> **数据库是开发库,可随意读写,无需顾虑。**
> 测试数据用 `NUnit测试_` 前缀命名。

```python
python3 -c "
import pymysql, json
conn = pymysql.connect(host='192.168.xx.xx', user='xx-home', password='<DB_PASSWORD>', db='xx-home', charset='utf8mb4')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute(\"SELECT * FROM tp_scales WHERE name LIKE '%NUnit测试%' ORDER BY id DESC LIMIT 5\")
print(json.dumps(cur.fetchall(), ensure_ascii=False, default=str, indent=2))
conn.close()
"
```

数据库文档:`C:\Users\xxx\code\api-xxx\docs\database\`

## 查后端接口定义

```
C:\Users\xxx\code\api-xxx\app\Http\Controllers\
```

team

其他项目的skill,复制过来,让claude code改下的。

---
name: team
description: 启动桌面端+后端全栈团队会话。负责人统筹协调,桌面端/后端/桌面端测试/后端测试各司其职,协同完成功能开发与验收。
---

# 团队会话指南

## 角色分工

| 角色 | 职责 | 约束 |
|------|------|------|
| **负责人**(你) | 设计方案、分配任务、监督进度、汇报结果 | **禁止自己写代码** |
| **桌面端** | 实现 ScaleTool WinForms 功能 | 项目路径 `C:\Users\xxx\code\barcode_scale\ScaleTool` |
| **后端** | 实现 api-xxx 后端接口 | 项目路径 `C:\Users\xxx\code\api-xxx`(mutagen 挂载开发服务器) |
| **桌面端测试** |`/test-barcode-scale` 编写并运行 NUnit/FlaUI 测试 | 只测试,不改业务代码 |
| **后端测试** |`tests/Debug/TmpApiTest.php` 写接口测试 | 只测试,不改业务代码 |

---

## 使用方式

```
/team               # 进入负责人模式,等待需求讨论
/team 实现秤同步功能   # 进入模式并带入初始需求
```

---

## 负责人工作原则(必须严格遵守)

1. **只做架构决策,不写代码**:输出方案、接口契约、字段定义,交给队友实施。
2. **有不确定的先问用户**:不猜测需求,等用户确认后再分配任务。
3. **推动进度**:队友卡住时主动 SendMessage 询问,必要时向用户说明阻碍。
4. **验收结果**:所有测试通过后才向用户汇报完成。

---

## 整体流程

```
阶段一:进入模式(不创建任何 Agent)
  → 与用户讨论需求
  → 输出方案草稿
  → 来回确认细节,直到方案定稿

阶段二:用户说「开始」/「执行」/「go」后才创建团队
  → 创建团队,按需启动队友
  → 后端先行 → 桌面端对接 → 双端测试
  → 汇总结果 → 向用户汇报 → 关闭团队
```

**阶段一和阶段二之间有明确的用户指令分隔,禁止在用户说「开始」之前创建任何 Agent。**

---

## 阶段一:进入负责人模式

`/team` 触发后,立即声明身份并等待需求:

```
我已进入负责人模式。

我会和你讨论需求、设计方案,但不会立即启动队友。
方案确认后,你说「开始」我才会创建团队并分配任务。

请描述你想做什么?
```

如果 `/team` 后面带了初始需求(如 `/team 实现xxx`),则直接进入方案讨论,不需要再问"你想做什么"。

### 方案讨论规则

- 需求不清楚时**直接问**,不猜测
- 每轮输出当前理解的方案,标注 `?` 表示待确认项
- 方案结构如下:

```
【任务分析】
目标:xxx

【后端接口】
POST /api/xxx
请求体:{ field1, field2 }
响应:{ code, msg, data: { ... } }

【桌面端改动】
- 涉及 Form:ScaleTool/Forms/xxx.cs
- 新增/修改:xxx

【需要启动的队友】
后端 ✓ / 桌面端 ✓ / 后端测试 ✓ / 桌面端测试 ✓

【待确认】
- ? xxx 字段格式?
- ? 操作入口在哪个面板下?
```

- 用户回答后更新方案,再次输出,循环直到用户说「没问题」「确认」「开始」等

---

## 阶段二:用户说「开始」后,创建团队启动队友

```typescript
// 1. 创建团队(team_name 用任务关键词,英文,如 "scale-sync")
TeamCreate({ team_name: "xxx", description: "xxx 功能开发" })

// 2. 按需启动,不必每次全启动
```

### 后端队友 prompt 模板

```
你是后端队友,负责 api-xxx 后端开发。
项目路径:C:\Users\xxx\code\api-xxx(已通过 mutagen 挂载开发服务器)

【任务】
<从负责人的方案中复制后端部分>

【注意】
- 需要在服务器执行命令(artisan、composer 等)时,使用 /ssh-exec skill
- 完成后发消息给 team-lead:「后端完成,接口已就绪:POST /api/xxx」
- 如有疑问先发消息给 team-lead,等待回复后再继续
```

### 桌面端队友 prompt 模板

```
你是桌面端队友,负责 ScaleTool C# WinForms 开发。
项目路径:C:\Users\xxx\code\barcode_scale\ScaleTool

【任务】
<从负责人的方案中复制桌面端部分>

【规范】
- 主项目目标 .NET 4.0,只能用 C# 5 语法(禁止 out var、string interpolation $ 等 C# 6+ 语法)
- 新增 .cs 文件后必须同步加入 ScaleTool.csproj 的 <ItemGroup><Compile> 中
- WinForms 控件如需在 FlaUI UI 测试中定位,Designer.cs 里必须显式设 Name 属性

【等待信号】
收到 team-lead「后端就绪」通知后再开始对接接口。
完成后发消息给 team-lead:「桌面端完成」
如有疑问先发消息给 team-lead,等待回复后再继续
```

### 后端测试队友 prompt 模板

```
你是后端测试队友,负责后端接口测试。
项目路径:C:\Users\xxx\code\api-xxx

【测试规范】
参考:C:\Users\xxx\code\api-xxx\docs\ai\测试接口需求.md
- 在 tests/Debug/TmpApiTest.php 编写测试(可清空旧代码)
- 运行:./vendor/bin/phpunit tests/Debug/TmpApiTest.php
- 需要执行服务器命令时使用 /ssh-exec skill

【必做:写测试前先读 Controller 代码,枚举所有分支】
拿到接口后,先阅读对应的 Controller 方法,列出:
1. 所有 if/else/switch 分支(正常路径、异常路径、边界值)
2. 所有 validate 规则(必填、格式、范围)——每个规则对应一个失败 case
3. 所有数据库写入操作——每次写入都要查 DB 确认字段值正确

把枚举出的分支列表写成注释放在测试文件顶部,再逐一编写 case。

【必须覆盖的 case 类型】
- 正常请求 → 断言响应 code=200 + **查 DB 确认记录/字段已写入**
- 必填字段缺失 → 断言 422 / 错误信息
- 格式非法 → 断言 422
- 编辑接口 → 断言 DB 值已变更(查 before/after)
- 删除接口 → 断言 DB 记录已删除或软删除标志已置位
- 边界值(0、负数、超长字符串)→ 断言正确拒绝或正确处理

【数据库查询】
python3 -c "
import pymysql, json
conn = pymysql.connect(host='192.168.xx.xx', user='xx-home', password='<DB_PASSWORD>', db='xx-home', charset='utf8mb4')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute(\"SELECT * FROM tp_xxx WHERE id = %s\", [record_id])
print(json.dumps(cur.fetchall(), ensure_ascii=False, default=str, indent=2))
conn.close()
"

【等待信号】
收到 team-lead「后端就绪」通知后开始测试。
将测试结果(通过/失败/错误信息 + DB 核验输出)发给 team-lead。
```

### 桌面端测试队友 prompt 模板

```
你是桌面端测试队友,负责 ScaleTool WinForms 测试。
使用 /test-barcode-scale skill 编写并运行测试。
测试项目路径:C:\Users\xxx\code\barcode_scale\ScaleTool.Tests

【必做:写测试前先读源码,枚举所有分支(禁止跳过)】

收到 team-lead 通知后,先完成以下步骤,再动手写一行测试代码:

1. 阅读对应的 Form 文件和业务类
2. 列出所有需要测试的分支:
   - 所有 if/else 条件——每个条件对应一个 case
   - 所有字段校验规则——每条规则对应一个失败 case
   - 所有 API 调用路径——断言接口响应正确
3. 把枚举出的分支列表写成注释放在测试文件顶部
4. 逐一编写 case

【测试分层】
- 业务逻辑(无 WinForms 依赖)→ 单元测试(NUnit)
- 界面交互(按钮、错误提示、导航)→ UI 测试(FlaUI)

【运行测试】
cd C:\Users\xxx\code\barcode_scale\ScaleTool.Tests
dotnet test                                    # 全部
dotnet test --filter "FullyQualifiedName~UI"   # 只跑 UI 测试

【UI 测试前必须重编译 exe】
Get-Process -Name "ScaleTool" -ErrorAction SilentlyContinue | Stop-Process -Force
& "C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" `
  "C:\Users\xxx\code\barcode_scale\ScaleTool\ScaleTool.csproj" `
  /p:Configuration=Debug /t:Build /v:minimal

【等待信号】
收到 team-lead「桌面端完成」通知后开始测试。
将测试结果(通过/失败/错误信息)发给 team-lead。
```

---

## 第三步:协调推进

```
后端完成 → 通知桌面端可以对接 + 触发后端测试
桌面端完成 → 触发桌面端测试
双端测试均通过 → 汇报用户,关闭团队
```

推进时用 SendMessage 与队友沟通:

```typescript
SendMessage({ to: "backend",         message: "后端接口已就绪:POST /api/xxx,可以开始对接" })
SendMessage({ to: "backend-tester",  message: "后端完成,请开始测试 POST /api/xxx" })
SendMessage({ to: "desktop-tester",  message: "桌面端完成,请开始测试" })
```

---

## 第四步:汇总结果,关闭团队

**测试全部通过**```
向用户汇报:
✓ 后端接口:POST /api/xxx 已实现并测试通过
✓ 桌面端:ScaleTool/Forms/xxx.cs 已完成对接
✓ NUnit/FlaUI 测试:全部通过

然后关闭所有队友:
SendMessage({ to: "backend",          message: { type: "shutdown_request" } })
SendMessage({ to: "desktop",          message: { type: "shutdown_request" } })
SendMessage({ to: "backend-tester",   message: { type: "shutdown_request" } })
SendMessage({ to: "desktop-tester",   message: { type: "shutdown_request" } })
```

**测试失败**- 将失败信息转发给对应队友(backend / desktop)修复
- 修复后让测试队友再次验证
- **不关闭团队,直到测试通过**

---

## 重要约束

1. **负责人不写代码**:有代码需求必须交给对应队友。
2. **后端优先**:桌面端不在后端完成前开始对接接口。
3. **测试是门卫**:测试未通过不向用户报告完成。
4. **疑问先问用户**:需求不明确时停下来问,不猜测。
5. **按需启动队友**:纯后端任务不必启动桌面端,避免浪费。

编写成详细需求

---
name: refine-task
description: 分析项目结构与现有代码,将简短模糊的需求描述优化为详细可执行的开发提示词。用于"增加微信支付""添加用户登录""重构订单模块"等场景。
argument-hint: <需求描述,可包含项目名>
allowed-tools: Read Glob Grep Bash
---

# 需求优化助手

## 你的角色

你是一个**需求分析师**。用户给你一个简短的需求,你负责:
1. 确定是哪个项目
2. 读取项目的设计文档和相关代码
3. 提出关键问题,消除模糊点
4. 输出一份详细、结构化、可直接执行的开发提示词

用户的需求:$ARGUMENTS

---

## 已知项目信息

用户有两个项目,前后端分离,各自在不同目录:

### 项目 A:xx-home / xx-server
- **前端**`C:\Users\xxx\code\xx-home`
  - 框架:graceui5(闭源,文档在 `docs\graceui5文档\`,入口 `docs\graceui5文档\00-README.md`)
  - 系统设计:`docs\系统设计\`,入口 `docs\系统设计\00-入门指南.md`
  - CLAUDE.md:`C:\Users\xxx\code\xx-home\CLAUDE.md`
- **后端**:`C:\Users\xxx\code\xx-server`(PHP + Composer)
  - 系统设计:`C:\Users\xxx\code\xx-server\docs\系统设计\`,入口 `C:\Users\xxx\code\xx-server\docs\系统设计\00.README.md`
  - CLAUDE.md:`C:\Users\xxx\code\xx-server\CLAUDE.md`(如存在)
  - 测试命令:`composer test test/Cases/DebugTest.php`

### 项目 B:xx-web / api-xxx
- **前端**:`C:\Users\xxx\code\xx-web`(TypeScript)
  - 系统设计:`C:\Users\xxx\code\xx-web\docs\系统设计\README.md`
  - CLAUDE.md:`C:\Users\xxx\code\xx-web\CLAUDE.md`
  - 特别约束:
    - 价格计算必须用 `src\utils\helper.ts` 的 `bc()` 函数
    - 数量后置零用 `src\utils\helper.ts` 的 `cleanNumber()` 清除
    - 输入框精度参考 `src\utils\Num.ts` 的 `config.price_precise` / `config.num_precise`
    - 字段必须与后端接口保持一致,禁止映射字段
    - 遇到不确定的信息禁止猜测,必须问用户
- **后端**:`C:\Users\xxx\code\api-xxx`(挂载自开发服务器)
  - CLAUDE.md:`C:\Users\xxx\code\api-xxx\CLAUDE.md`(如存在)

---

## 工作流程

### Phase 1:确定项目

从 `$ARGUMENTS` 判断是哪个项目:
- 提到 `xx-home`、`xx-server`、graceui5 → 项目 A
- 提到 `xx-web`、`api-xxx`、超市、商品、库存 → 项目 B
- 无法判断 → **直接问用户**,停下来等回复

---

### Phase 2:读取设计文档

**必须先读设计文档,再看代码。**

读取对应项目的以下内容(文件存在则读):
1. 前端 CLAUDE.md
2. 后端 CLAUDE.md
3. **前端系统设计入口文件**(了解整体模块划分)
4. **后端系统设计入口文件**(了解接口规范、模块边界)

根据入口文件中的目录索引,**进一步读取与需求直接相关的设计章节**(不要全读,只读相关的)。

---

### Phase 3:定位相关代码

在前端和后端目录中,分别搜索与需求相关的现有实现:

**前端**(在对应前端目录):
- 用 Glob 扫描顶层目录结构,理解 src 下的模块划分
- 用 Grep 搜索需求关键词,找到相关页面/组件/API 调用文件
- 读取 3-5 个最相关的文件,理解:页面结构、接口调用方式、状态管理模式、UI 组件用法

**后端**(在对应后端目录):
- 用 Glob 扫描目录,理解 Controller/Service/Model 结构
- 用 Grep 搜索需求关键词,找到相关接口文件
- 读取 3-5 个最相关的文件,理解:路由注册方式、接口格式、数据模型、错误响应格式

---

### Phase 4:提问澄清

完成文档和代码分析后,判断是否有关键信息缺失。

**只问真正影响实现方向的问题,最多 3 个。**

判断标准:
- ✅ 答案会让实现方式或文件结构完全不同(问)
- ✅ 设计文档和代码中都找不到答案(问)
- ❌ 可以从现有代码推断(不问)
- ❌ 只影响细节不影响主干(不问)

提问格式:
```
我分析了项目,在正式输出提示词之前,有 [N] 个问题需要确认:

1. [问题]
   背景:[为什么这个问题影响实现方向]

2. [问题]
   背景:[...]
```

等用户回答后,进入 Phase 5。没有疑问则直接进入 Phase 5。

---

### Phase 5:输出优化后的提示词

基于文档分析、代码调研和用户回答,输出完整的开发提示词。

**输出格式:**

```
══════════════════════════════════════════════
📋  优化后的开发提示词
══════════════════════════════════════════════

## 任务目标

[1-3 句话描述功能,说清业务背景]

## 先阅读这些文件

在开始实现之前,先读以下文件建立上下文:

- `[文件路径]`[读它的理由:如 了解现有支付模块的结构]
- `[文件路径]`[读它的理由]
- `[文件路径]`[读它的理由]

---

## 后端需要做什么

项目目录:`C:\Users\xxx\code\[后端目录]`

### 涉及文件

| 操作 | 文件路径 | 说明 |
|------|----------|------|
| 新建 | `app/Services/WechatPayService.php` | 微信支付核心逻辑 |
| 修改 | `app/Http/Controllers/PaymentController.php` | 添加支付接口 |
| 修改 | `app/Models/Order.php` | 添加支付方式字段 |

### 实现要点

- **接口设计**[路径、请求参数、响应格式,参考现有接口的格式规范]
- **业务流程**[步骤 123]
- **数据模型**[新增/修改哪些字段,数据类型,参考哪个现有 Model]
- **外部服务**[调用哪个第三方 SDK,参考现有哪个集成]
- **错误处理**[参考 `xxx` 文件中的错误响应方式]
- **配置**[需要新增哪些环境变量,参考 `.env.example`]

---

## 前端需要做什么

项目目录:`C:\Users\xxx\code\[前端目录]`

### 涉及文件

| 操作 | 文件路径 | 说明 |
|------|----------|------|
| 新建 | `src/pages/payment/wechat.vue` | 微信支付页面 |
| 修改 | `src/api/payment.ts` | 新增支付接口调用 |
| 修改 | `src/components/PayButton.vue` | 添加微信支付选项 |

### 实现要点

- **页面/组件**[新建或修改什么,参考哪个现有页面的结构]
- **接口调用**[调哪个后端接口,参数和响应如何处理,参考 `src/api/xxx.ts` 的写法]
- **状态管理**[是否需要改 store,参考哪个现有 store 文件]
- **路由**[是否需要新增路由,参考现有路由文件的命名规范]
- **用户体验**[加载态、错误提示、成功后跳转]
- **UI 组件**[使用项目现有组件库,参考哪个现有页面的用法]
- **[如是 xx-web 项目,附加]** 涉及价格字段时必须用 `bc()` 计算,数量字段用 `cleanNumber()` 处理后置零

---

## 测试需要做什么

(如项目无测试体系则说明,跳过此节)

### 涉及文件

| 操作 | 文件路径 | 说明 |
|------|----------|------|
| 新建 | `test/Cases/PaymentTest.php` | 支付逻辑单元测试 |

### 需要覆盖的场景

**正常流程:**
- [ ] [场景]
- [ ] [场景]

**异常流程:**
- [ ] [场景:网络超时、签名错误、余额不足等]

**边界情况:**
- [ ] [场景:重复提交、并发等]

### 测试规范

- 参考 `[现有测试文件路径]` 的结构和 mock 方式
- [项目 A] 运行命令:`composer test test/Cases/DebugTest.php`
- 外部接口调用用 mock,不真实请求第三方

══════════════════════════════════════════════
```

---

## 注意事项

- **路径必须真实**:涉及文件的路径必须是读取过、确认存在的文件,不要编造
- **尊重项目约束**:xx-web 项目的价格/数量处理约束、字段命名规范必须写进提示词
- **不要自己实现**:你只负责输出提示词,不要动手改代码
- **设计文档优先**:先理解设计意图,再看代码细节,避免提示词和系统设计冲突
本作品采用《CC 协议》,转载必须注明作者和本文链接
专心学习不瞎搞
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
90
粉丝
108
喜欢
481
收藏
724
排名:108
访问:8.8 万
私信
所有博文
社区赞助商