尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
PowerBuilder DataWindow 数据库开发机制与避坑
简介面向PowerBuilder数据库开发实践的案例资料包适合已具备基础语法、希望借助完整项目吃透数据窗口与事务控制的开发者。内容围绕数据窗口设计、BeforeOpen/AfterOpen等事件处理、COMMIT与ROLLBACK事务管理、错误调试、报表图表、ODBC与ADO.NET数据源连接、对象库复用及存储过程调用展开并借助图书、财务、进销存、酒店、人力资源等业务库还原真实项目场景兼顾界面布局与代码维护等工程细节。压缩包共172个文件、约7.42MB以bmp/ico/jpg界面素材、sql脚本、pbl/pbd/pbt/pbw工程与库文件、dll组件、exe程序及mdf/ldf数据库文件为主体辅以bak备份与ini配置解压后可还原案例环境并对照源码复现示例。目前已有244人学习读者可获得可运行工程、建库脚本与界面资源便于按模块拆解业务逻辑、积累PB项目实战经验。1. 结论先给PowerBuilder 的数据库开发为什么快得不像话第一次翻 PowerBuilder 的数据库代码不少人会觉得不真实一个带增删改查、主从表联动、条件过滤的界面窗口脚本加起来不到三十行还找不到一条 INSERT 语句。流行的解释是「PB 封装得好」这个说法太糊封装的到底是什么没说清。真正的机制是PowerBuilder 把 SQL 的生成权从数据库端换到了客户端——DataWindow 对象在定义阶段就把结果集的列名、类型、更新目标表、主键列、WHERE 策略全部存成了一份元数据运行时它拿着这份元数据自己拼 SELECT、自己拼 UPDATE自己在内存里记账哪一行被改过。理解这条链路你才能明白为什么 PB 数据库开发里有那么多“必须按它的规矩来”的地方也才看得懂那份经典案例资源里每一处dw_1.Update()背后到底在发生什么。2. DataWindow 的真身一条 SELECT 是怎么变成客户端内存里的结果集的2.1 从定义到检索SQL 在哪个时刻被拼出来多数人以为 DataWindow 是个“绑定了数据库的表格控件”这个认知会把后面所有事情都带偏。DataWindow 在 PBL 库里保存的是一份定义不是数据也不是一个活着的连接。这份定义包含两大部分结果集描述每一列的列名、类型、长度、是否可空、显示格式、编辑风格和 SQL 语句一个 SELECT可能带检索参数。结果集描述来自建库时的数据字典。画 DataWindow 的时候 PB 连一次数据库把表的列元数据读回来缓存进对象之后你在画笔里改布局、调显示格式、按住鼠标拖列宽全程不需要数据库连得上。这也是为什么把对象导出成文本后能在.srd文件里看到table(... column(...) )这种段落——它就是那份元数据的明文形态。检索发生的时刻才是 SQL 真正被拼出来并送给数据库的时刻long ll_rows string ls_dept ls_dept sle_dept.text // SetTransObject 把外部事务对象挂到 DataWindow 上 dw_emp.SetTransObject(sqlca) // Retrieve 的参数顺序严格对应 DataWindow 定义里检索参数的顺序 ll_rows dw_emp.Retrieve(ls_dept) IF ll_rows 0 THEN // 负数代表失败具体原因在事务对象里 MessageBox(检索失败, sqlca.SQLErrText) RETURN END IFRetrieve()返回的是实际拉回来的行数失败返回 -1。这里有个容易被忽略的机制PB 把 SELECT 里的检索参数当作绑定变量处理WHERE dept_id :ls_dept里的冒号参数不会在前端做字符串拼接而是走数据库接口的绑定通道。所以正常情况下检索参数这条路是安全的真正危险的是下面这种运行时改 SQL 的写法string ls_sql, ls_cond // Describe 取出定义里保存的原始 SELECT 文本 ls_sql dw_emp.Describe(DataWindow.Table.Select) // 手工切掉旧 WHERE 再拼新的——这里就是注入的入口 ls_cond where dept_id sle_dept.text ls_sql ls_sql ls_cond dw_emp.SetSQLSelect(ls_sql) dw_emp.Retrieve()SetSQLSelect()改的是对象定义里的 SQL 文本改完必须重新Retrieve()才生效。它有两个副作用要记住一是改了 SELECT 之后原本在 Update Properties 里配好的列映射可能对不上Update()会直接失败二是Describe拿到的原始 SQL 未必带 WHERE也可能已经带了一个 WHERE硬拼很容易拼出语法错误。我一般宁可在 DataWindow 里预先把条件写成(:cond OR dept_id :cond)这种可空参数形式也不在运行时切字符串。2.2 三级缓冲区与四种行状态DataWindow 怎么记住你改过哪一行结果集拉回来之后全部落在客户端内存里。这是 PB 最核心也最容易被低估的设计DataWindow 内部维护三个缓冲区每个缓冲区里每一行、每一列都存两份值。缓冲区装什么怎么进去Primary!当前有效、参与显示和计算的行Retrieve 拉回来的、InsertRow 新插的Filter!被过滤掉但还没丢弃的行Filter()或SetFilter()Filter()Delete!被标记删除、还没提交的行DeleteRow()两份值分别是 Current当前值和 Original原始值。Original 在检索回来时等于 CurrentUpdate()成功并接受之后会被重置为新的 Current。GetItem 系列函数有个隐藏的第四参数就是用来指定读哪一份的。行状态有四种全部由 PB 在SetItem()被调用的瞬间自动打标不需要你操心NotModified!——检索回来没动过Update()时被跳过DataModified!——老行被改过Update()时走 UPDATENew!——新插入但还没赋过值Update()时被跳过NewModified!——新插入且赋过值Update()时走 INSERT这套标记机制解释了 PB 数据库开发里一个很反直觉的现象你在界面上改了二十行、删了三行、插了两行点保存PB 只发出去 22 条语句其余几百行一个字节都不发。不是它聪明是它一直在记账。long ll_row // 在末尾插一个空行 dw_emp.InsertRow(0) ll_row dw_emp.RowCount() // 赋值之后这一行的状态会自动从 New! 变成 NewModified! dw_emp.SetItem(ll_row, emp_name, 某员工) dw_emp.SetItem(ll_row, dept_id, D001) // 第三个参数传 0 表示取整行状态第四个参数 TRUE 表示看原始值 IF dw_emp.GetItemStatus(ll_row, 0, Primary!, TRUE) NewModified! THEN // 这行会在 Update 时走 INSERT END IF2.3 用 PowerScript 直接摸缓冲区代价和边界既然缓冲区就在内存里PowerScript 当然可以直接遍历。做法不复杂但代价要心里有数long ll_i, ll_cnt string ls_now, ls_org long ll_changed ll_cnt dw_emp.RowCount() ll_changed 0 FOR ll_i 1 TO ll_cnt // 当前值 ls_now dw_emp.GetItemString(ll_i, emp_name) // 原始值第四个参数 TRUE ls_org dw_emp.GetItemString(ll_i, emp_name, Primary!, TRUE) IF ls_now ls_org THEN ll_changed ll_changed 1 END IF NEXT MessageBox(变更行数, String(ll_changed))这段代码功能上没问题但每一行要调两次GetItemString每次都是一次跨对象消息分发几千行下来开销肉眼可见。真要大批量扫描有三条路更划算一是直接用Describe(Evaluate(...))在 DataWindow 内部算聚合值二是把判断逻辑放回 SQL让数据库算完只回一个数三是批量操作前后用SetRedraw(FALSE)/SetRedraw(TRUE)包住只刷一次界面。还有一个边界缓冲区的容量等于结果集的大小。DataWindow 默认是一次性把整个结果集全拉下来不做惰性加载。表小的时候这是天大的优势几万行以上就会开始吃内存和网络。所以生产环境里凡是可能出大结果集的 DataWindow我都会在定义里设一个检索行数上限或者干脆改成按条件分页查。3. 提交链路Update() 到底把哪些字节发给了数据库3.1 Update Properties 里那三个必须手填的开关Update()能工作的前提是 DataWindow 定义里那段 Update Properties 配置齐全。它有三个必填项缺一个就会在运行时给你难看第一是Table to Update。哪怕你的 SELECT 只查了一张表这里也必须明确指定更新目标。如果 SELECT 是两张表 join 出来的这一项更是唯一能告诉 PB“到底改哪张表”的地方不填的话Update()直接返回 -1而且错误信息往往含糊得让人抓狂。第二是Key Columns。勾选哪几列作为主键决定了 WHERE 子句长什么样。如果一列都不勾PB 会退化成一堆列全塞进 WHERE既慢又容易误判。第三是Updateable Columns。哪些列允许出现在 SET 里。这里我一般会刻意把创建时间、创建人这类列排除掉避免前端误改。配置正确的情况下Update()生成的语句基本就是这样的形状-- 由 DataWindow 根据 Update Properties 自动生成? 是绑定变量 UPDATE employee SET emp_name ?, salary ? WHERE emp_id ?对应到脚本这一侧最重要的一个事实是Update()不提交。它只负责把变更发出去提交权在事务对象手上。// 第一个参数 accept成功后把行状态重置为 NotModified并清空 Delete 缓冲区 // 第二个参数 resetflag成功后是否重置更新标志 IF dw_emp.Update(TRUE, TRUE) 1 THEN COMMIT USING sqlca; IF sqlca.SQLCode 0 THEN MessageBox(提交失败, sqlca.SQLErrText) END IF ELSE ROLLBACK USING sqlca; MessageBox(更新失败, sqlca.SQLErrText) END IFaccept参数值得单独说一句。传 TRUEPB 会把已经写成功的行状态统一压回NotModified!把 Delete 缓冲区清空并把 Original 值更新成 Current 值传 FALSE状态原样保留下次Update()会把同样的行再发一遍。除非你在做“先试试看能不能写”这种探测否则一律传 TRUE。3.2 Update Where Clause 三种模式并发冲突到底在哪一层被拦下Update Properties 里那个 Where Clause 下拉框是 PB 数据库开发里设计取舍最集中的一处。三种模式生成的 WHERE 差别巨大模式生成的 WHERE拦得住什么代价Key Columns只用主键列 原始值拦不住并发覆盖WHERE 最短最快Key and Updatable Columns主键 所有可更新列 原始值别人改过任何一列都会失配WHERE 很长大字段列不能参与比较Key and Modified Columns主键 本次被改的列 原始值只在“改的是同一列”时失配折中但漏检跨列覆盖Key Columns是最省事的也是最容易出事的A 读到工资 5000B 也读到 5000B 先改成 6000 提交A 后改成 5500 提交A 的 UPDATE 照样匹配到行B 的修改被无声抹掉。这就是典型的后写覆盖先写日志里连个痕迹都没有排查起来纯粹靠猜。Key and Updatable Columns相当于把乐观锁做到了列级。B 改过之后A 的 WHERE 匹配不到任何行PB 会返回 -1、SQLCA.SQLNRows等于 0。这里有个必须分清的点Update()返回 -1 有两种完全不同的原因处理方式也不一样。IF dw_emp.Update(TRUE, TRUE) 1 THEN COMMIT USING sqlca; ELSE IF sqlca.SQLDBCode 0 AND sqlca.SQLNRows 0 THEN // 语句本身没报错但一行都没匹配到 —— 并发冲突 MessageBox(数据已变更, 有别的用户先改了这一行请重新检索后再改。) ELSE // 真的执行出错了看数据库原生错误码 MessageBox(更新出错, String(sqlca.SQLDBCode) ~r~n sqlca.SQLErrText) END IF ROLLBACK USING sqlca; END IF第二种模式有个隐藏的坑WHERE 里要比较的列如果包含 text、image、blob 这类大对象类型很多数据库根本不允许在 WHERE 里做等值比较语句会直接报错。这时候只能退回到Key and Modified Columns或者干脆不用 DataWindow 的自动更新自己写 UPDATE。3.3 SetTrans 与 SetTransObject一个字母之差事务控制权易主这两个函数名只差一个词行为差得远是 PB 数据库开发里最经典的一道分水岭。// 方式一SetTrans —— 把连接参数“复制”进 DataWindow连接归它自己管 dw_emp.SetTrans(sqlca) dw_emp.Retrieve() // 内部自己 CONNECT检索完自己 DISCONNECT // 方式二SetTransObject —— 共享外部事务对象连接归你管 CONNECT USING sqlca; IF sqlca.SQLCode 0 THEN MessageBox(连接失败, sqlca.SQLErrText) RETURN END IF dw_emp.SetTransObject(sqlca) dw_emp.Retrieve()SetTrans干的事情是把事务对象里的连接参数值抄一份给 DataWindow之后每次Retrieve()或Update()它内部自己建连接、自己执行、自己提交、自己断开。这个“自己提交”是所有麻烦的源头你以为Update()之后能ROLLBACK实际上数据已经落库了后悔药没得吃。而且每条语句建一次连接主从表联动的场景下开销会成倍放大。SetTransObject是反过来的连接由你CONNECT事务由你COMMIT/ROLLBACK多个 DataWindow 共享同一个sqlca主表和明细表的更新要么一起成功要么一起回滚。代价是你得记住COMMIT——忘了它行锁会一直挂着别人动不了你自己下次也动不了。还有一处容易忽视的开关sqlca.AutoCommit。默认 FALSE一旦设成 TRUE连嵌入式 SQL 都会逐条自动提交ROLLBACK同样失效。这个属性我基本不碰除非在跑批量 DDL。4. 绕开 DataWindow 的时刻嵌入式 SQL 与游标的底层状态机4.1 游标的状态机与 SQLCode 100DataWindow 适合“拉一批数据到界面上编辑”但有些活它干不了或者干起来别扭逐行做复杂计算、批量更新几十万行、调用带出参的存储过程。这时候就得退回嵌入式 SQL。单行查询用SELECT ... INTO取不到数据时SQLCode是 100不是错误string ls_name long ll_id ll_id Long(sle_id.text) SELECT emp_name INTO :ls_name FROM employee WHERE emp_id :ll_id USING sqlca; IF sqlca.SQLCode 100 THEN MessageBox(提示, 没有这个工号) RETURN ELSEIF sqlca.SQLCode -1 THEN MessageBox(查询出错, sqlca.SQLErrText) RETURN END IF多行就得上游标。游标在 PB 里的本质是一台四状态机DECLARE 只是声明不执行OPEN 才真正把语句发给数据库、在服务端建立结果集FETCH 一次取一行取空了返回 100CLOSE 释放服务端资源。long ll_count string ls_name, ls_dept ls_dept sle_dept.text ll_count 0 DECLARE cur_emp CURSOR FOR SELECT emp_name FROM employee WHERE dept_id :ls_dept ORDER BY emp_id; OPEN cur_emp; IF sqlca.SQLCode 0 THEN MessageBox(打开游标失败, sqlca.SQLErrText) RETURN END IF DO WHILE TRUE FETCH cur_emp INTO :ls_name; IF sqlca.SQLCode 100 THEN EXIT // 正常取完退出循环 ELSEIF sqlca.SQLCode 0 THEN EXIT // 真错误 END IF ll_count ll_count 1 // 这里做逐行业务处理 LOOP CLOSE cur_emp;这里有两个血泪经验。第一判断结束只能用SQLCode 100不能拿SQLNRows当条件SQLNRows在一些数据库驱动上是批量提交时才填的取单行时可能是 0 也可能没更新。第二游标打开期间尽量不要在同一个事务对象上做 DML某些数据库会直接报“游标已失效”尤其在有隔离级别要求的环境里。4.2 错误三件套SQLCode / SQLDBCode / SQLErrTextPB 的数据库错误信息全在事务对象里三个字段分工明确缺一个都查不清问题SQLCode——PB 层的结论0 成功、100 无数据、-1 失败SQLDBCode——数据库返回的原生错误码-1 的时候才需要看它SQLErrText——数据库返回的原始错误文本通常是英文但信息量最大SQLNRows——受影响或检索到的行数SQLReturnData——存储过程的返回值我的习惯是写一个统一的检查函数所有嵌入式 SQL 后面跟一句调用避免到处复制粘贴判断逻辑// f_check_sqlca 的片段统一处理事务对象状态 // 入参 as_action 是业务动作名只用于提示文案 IF sqlca.SQLCode -1 THEN MessageBox(数据库错误 - as_action, 原生错误码: String(sqlca.SQLDBCode) ~r~n~r~n sqlca.SQLErrText) ROLLBACK USING sqlca; RETURN -1 ELSEIF sqlca.SQLCode 100 THEN RETURN 100 // 无数据不算错交给调用方决定怎么处理 END IF RETURN 0注意SQLCode 100只出现在SELECT INTO和FETCH上。UPDATE/DELETE影响 0 行时SQLCode仍然是 0得看SQLNRows——这是很多人第一次遇到“明明没改到任何行程序却报成功”时的困惑来源。4.3 动态 SQL 的四种格式与存储过程调用表名、列名在编译期不确定的场景得用动态 SQL。PB 把它分成四种格式从简单到复杂格式一EXECUTE IMMEDIATE不能带参数、不能有结果集DDL 和固定条件的 DML 用它最省事string ls_sql ls_sql DELETE FROM temp_result WHERE batch_no B20240101 EXECUTE IMMEDIATE :ls_sql;格式二PREPAREEXECUTE ... USING带参数但没有结果集参数走绑定安全string ls_sql long ll_id, ll_amt ls_sql UPDATE account SET balance balance - :amt WHERE id :id PREPARE sql_upd FROM :ls_sql; EXECUTE sql_upd USING :ll_amt, :ll_id;格式三PREPAREDECLARE CursorOPEN ... USINGFETCH结果集的列在编译期已知这是动态 SQL 里最常用的形态。格式四在此基础上多了DESCRIBE和描述符变量用于结果集的列数、类型完全未知的通用查询——写起来相当繁琐除非在做通用的数据导出工具一般用不到。存储过程的调用形式和游标很像只是把CURSOR换成PROCEDUREdouble ld_result // 声明一个存储过程调用参数按定义顺序绑定 DECLARE proc_calc PROCEDURE FOR sp_calc_bonus(:ll_emp_id, :ld_month) USING sqlca; EXECUTE proc_calc; IF sqlca.SQLCode 0 THEN MessageBox(执行失败, sqlca.SQLErrText) CLOSE proc_calc; RETURN END IF // 有返回结果集时用 FETCH 取没有则跳过 FETCH proc_calc INTO :ld_result; CLOSE proc_calc;要留意的是存储过程返回多个结果集时FETCH只认第一个后面的要靠CLOSE触发继续取具体行为随数据库接口变化很大。跨库迁移过一版代码的人多半在这上面踩过坑。5. 避坑与排查五个让 PB 数据库开发翻车的现场5.1 现象保存提示成功退出程序再进来数据全没了原因几乎总是漏了COMMIT。Update()只发语句不提交连接一断未提交的事务被数据库回滚。用SetTrans的人不容易撞上因为它自动提交一旦改成SetTransObject提交责任就转移到了你身上。解决方式是把提交写进统一的保存函数Update()返回 1 之后必须接着COMMIT USING sqlca并且检查SQLCode。主从表场景要把两张表的Update()都跑完再一次性提交中间任何一步失败就整体回滚别做半套。5.2 现象Update() 返回 -1SQLErrText 是空的SQLErrText为空、SQLDBCode为 0、SQLNRows为 0这三件事同时出现含义只有一个语句执行没报错但 WHERE 一行都没匹配到。常见于Key and Updatable Columns模式下别人先改了同一行或者主键列勾错了。解决方式是先按 5.2 那段代码把“并发冲突”和“真错误”分开处理再回头检查 Key Columns 是否勾的是真正的主键。如果业务上允许覆盖退回Key Columns模式如果不允许就在界面上提示用户重新检索别自作主张重试。5.3 现象两表 join 的 DataWindowUpdate() 一律失败现象是检索正常、编辑正常一保存就返回 -1。原因是 Update Properties 里的 Table to Update 没指定PB 不知道结果集里哪张表该被更新。join 出来的列里另一张表的列在它眼里没有更新归属。解决办法很直接在 Update Properties 里明确指定一张主表只勾选这张表的列作为 Updateable Columns。如果确实需要同时更新两张表就别指望一个 DataWindow 干完拆成主表一个 DataWindow、明细一个 DataWindow用同一个事务对象分两次Update()。5.4 现象界面上删掉的行重新检索又回来了DeleteRow()只是把行从 Primary 缓冲区挪进 Delete 缓冲区一个字节都没发给数据库。没调Update()就重新Retrieve()服务端的数据还在自然原样回来。解决办法是删除操作必须走完整的DeleteRow()→Update()→COMMIT三段。另外要记住Update()的accept参数传 TRUE 才会清空 Delete 缓冲区传 FALSE 的话下次Update()会把同一批删除再发一遍运气不好就是批量报错。5.5 现象换个数据库或者换个驱动中文全变问号这类问题跟 PB 本身关系不大根子在客户端字符集和数据库字符集没对齐。表现是检索出来全是乱码或者问号但用别的工具连同一个库看数据是好的。排查顺序是先确认数据库本身的字符集再确认 PB 的数据库接口配置里的字符集参数最后确认操作系统区域设置。三处必须一致。改完之后要重新建连接光重启程序不顶用有些驱动会把字符集缓存到进程级。6. 设计取舍把结果集缓存在客户端这笔账今天还怎么算把整个结果集搬到客户端、在内存里做行状态记账、由客户端拼 UPDATE——这套设计在当年是巨大的效率优势今天看则是一笔需要算清楚的账。优势侧很清楚。网络往返次数被压到最低一次 Retrieve 拿全量之后所有编辑、过滤、排序、汇总全在本地内存完成交互延迟跟数据库无关。行状态记账让“只提交变更”变成默认行为不需要写任何脏检查代码。DataWindow 的元数据还顺带解决了 UI 与数据结构的绑定问题改个显示格式不用动一行脚本。这套组合拳在局域网、中小数据量、以录入和查询为主的业务系统里效率高得离谱。代价侧同样清楚。结果集全量驻留内存几十万行的表直接顶不住得分页或者换架构。三段式更新Primary/Filter/Delete在多用户并发下必须靠 WHERE 子句做乐观锁模式选错就是静默覆盖。更麻烦的是业务逻辑的位置行状态记账发生在客户端意味着“什么算一次合法修改”这个判断分散在界面层服务端没有最终裁决权。后来三层架构流行本质就是把 DataWindow 往下沉让服务端持有结果集客户端只做展示。所以今天再看这套机制价值不在“照搬”而在它把数据库应用里最本质的几个问题摆得极其清楚结果集放在哪、变更怎么记账、并发怎么裁决、事务边界画在哪。这四个问题换个技术栈一样要回答只是现代框架把它们藏进了 ORM 和连接池里反而不容易看清。我自己的教训是早些年接手一个老系统看到SetTrans就想统一换成SetTransObject觉得性能更好、事务更可控。改到一半发现几十处保存逻辑都依赖“自动提交”这个隐含行为好几处Update()之后压根没有COMMIT。那批代码如果直接换上线当天就是数据丢失。从那以后我每次动事务模型之前都强制先把所有Update()调用点搜一遍确认每一处后面到底有没有配对的提交或回滚再决定改不改。这份案例资源的好处正在这里——它把主从表、单据明细、条件报表、权限分级这些典型结构都摆成了可运行的完整工程你可以对着源码把SetTransObject、Update Properties、缓冲区状态这些东西一个个验过去比看文档扎实得多。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

OpenMontage 开源视频剪辑自动化框架:从零搭建到批量渲染实战

OpenMontage 开源视频剪辑自动化框架:从零搭建到批量渲染实战

1. 从零搭建 OpenMontage:一个开源视频剪辑自动化框架的完整实践第一次看到 OpenMontage 这个名字,我下意识以为是某个视频剪辑软件的社区版。真正上手之后才发现,它其实是一套面向开发者的开源视频剪辑自动化框架——把时间线、轨道、素材、…

📅 2026/10/26 9:30:39
Cursor Agent Skill安装与优化全指南

Cursor Agent Skill安装与优化全指南

1. Cursor Agent Skill安装指南:从零到精通的完整实践作为一名长期使用Cursor的开发者,我深刻体会到Agent Skill生态对开发效率的提升作用。今天我将分享如何高效安装和管理Cursor的Agent Skill,以及如何根据实际开发需求定制专属技能包。1.1…

📅 2026/10/26 9:30:39
UniApp+Vue高校社团小程序开发实战与优化

UniApp+Vue高校社团小程序开发实战与优化

1. 项目概述:高校社团管理小程序的技术选型与价值 去年帮本地一所高校升级社团管理系统时,我们放弃了传统的PC端Web方案,转而采用uniappvue技术栈开发微信小程序。这个选择让注册率提升了47%,管理效率提高了60%。微信小程序天然具…

📅 2026/10/26 9:30:39
MORE NEWS

更多资讯

📰

没有需求文档怎么写代码?基于大模型与 Agent 的“自迭代”开发工作流探索

没有需求文档怎么写代码?基于大模型与 Agent 的“自迭代”开发工作流探索 摘要:传统软件工程将需求文档视为编码的前提,但在个人开发、原型验证与探索性项目中,“没有需求文档”才是常态。大模型与 Agent 的介入并未消解这一矛盾&…

📰

【附源码】基于SpringBoot的中国茶文化门户网站(源码+数据库+论文+答辩ppt一整套齐全)java开发springboot+vue框架javaweb,可做计算机毕业设计或课程设计

🙊作者简介:多年一线开发工作经验,分享技术代码帮助学生学习。自有计算机毕设的工作室团队,专注计算机毕设开发、定制、远程、文档编写指导等。 🍅 查看主页更多项目 | 计算机毕设工作室 🍅 🍅 …

📰

anthropics/knowledge-work-plugins:用 Markdown 和 JSON 把 Claude 定制成岗位专家的开源插件集

通用模型不懂你的公司。让 Claude 做一次财务对账或者销售 pipeline review,你得把术语、口径、审批流程从头讲一遍;让它去查 CRM、数据仓库、Jira,你得自己写一层集成。结果每个团队都攒下一堆 prompt 模板和一次性脚本,同一份活…

📰

2026-10-24-黑苹果WiFi网卡驱动折腾记录

2026-10-24-黑苹果WiFi网卡驱动折腾记录 背景 物理机黑苹果(OpenCore macOS 15 Sequoia),之前只有有线网络(Realtek RTL8136 PCIe 百兆网卡)。想买个 USB WiFi 网卡解放桌面,买了个 YICHIP 的 RTL8188EU 芯…

📰

寄存器位数与可见性速记

408计组|寄存器位数与程序员可见性:一篇可直接背的总结 一、先立两条公理 一切寄存器位数的推导,都建立在两个定义之上:机器字长 CPU 一次能并行处理的数据位数;地址位数 主存地址线(地址总线&#xff…

📰

悬赏作为合法性建构:鸽姆智库“贾子猜想”千年悬赏公告的学术社会学分析框架——以GG3M-KUCIUS-2026-001-A为分析锚点

标题 悬赏作为合法性建构:鸽姆智库“贾子猜想”千年悬赏公告的学术社会学分析框架 ——以GG3M-KUCIUS-2026-001-A为分析锚点 摘要 背景与问题。2026年10月23日,鸽姆智库(GG3M)通过CSDN博客平台发布编号为GG3M-KUCIUS-2026-001-A…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬