新闻详情

新闻详情

首页 / 资讯中心 / 详情

两个人同时点“审批通过”会怎样?状态迁移与并发控制实战

发布时间:2026/9/28 4:44:48来源:尧图网络
两个人同时点“审批通过”会怎样?状态迁移与并发控制实战
两个人同时点“审批通过”会怎样状态迁移与并发控制实战《企业级 Workflow 实战从审批流到 AI Agent》第 05 篇项目AcmeFlow星河设备客户维保服务开通中心本篇交付并发审批实验、资料版本冲突实验、PostgreSQL 条件更新方案、六张可编辑配图。1. 一次看似偶然的审批事故星河设备的一位客户提交了维保服务申请。运营审核员小周打开待办页面看见套餐、企业名称和上传材料都符合要求准备点击“通过”。同一时刻主管小林也打开了同一个待办。小林发现营业执照已经过期于是点击“驳回”。两个页面加载时都显示“待审核”两个操作者都认为自己的决定合法。如果后台只执行UPDATE applications SET status ? WHERE id ?两次写入可能都返回成功数据库最终只留下最后一次写入的状态。更麻烦的是短信、合同生成任务和审批历史可能分别记录了两个相反的决定。用户看到一个终态其他系统却按另一个终态继续工作。这不是前端按钮是否禁用的问题。按钮禁用只能阻止同一浏览器用户连续点击不能阻止不同设备、不同服务实例、网络重试或队列重复消费。即使页面发起请求之前再查询一次查询与写入之间仍有时间窗口。并发问题的核心是两个请求基于同一个旧事实发出命令时系统必须只有一个明确的裁决点。在本例中裁决点是保存流程实例的数据库事务。请求携带它看到的实例版本数据库只允许仍然满足该版本和当前状态的请求推进流程。图 1两个页面都基于 revision1 和资料 v1 做判断数据库对同一实例只接受一条符合条件的迁移。图中四格表示并发输入与裁决条件不表示页面 A 先于页面 B 执行。上篇的 AcmeFlow 已经有applications、workflow_instances、tasks和transition_history四类记录。本篇延续tenant_idxinghe-demo、business_keyCUSTOMER-001、application_id00000000-0000-4000-8000-000000000101这组教学数据。第 03 篇创建的申请在提交后处于SUBMITTED审核待办为OPEN。数据库中的workflow_instances.revision初始为 1applications.material_version初始为 1。我们只处理运营审核这一个阶段不在本篇接入签署、到账或 ERP。那些事实后来会继续决定是否能够进入READY和ACTIVE但不能由这次审核直接跳过。2. 先确定什么叫“只有一个结果”并发控制不能只检查最终state。假设 A 把状态改为APPROVEDB 随后把状态改为REJECTED最终状态确实只有一个值但业务已经发生了两次决定。正确的验收条件至少有四个同一资料版本只能形成一条有效审核决定流程实例版本只推进一次一个待办只被一个操作者完成历史记录能够说明胜出的命令和失败请求的处理结果。败方应得到可解释的冲突响应并重新读取最新资料与待办而不是收到“系统繁忙请无限重试”。这几个条件应在同一个数据库事务内成立。成功分支修改实例状态与版本完成待办写入迁移历史然后提交。若任何一步失败整笔事务回滚。把“写历史”放到事务之外会形成一个危险窗口主状态已经批准审计时间线却没有记录事后无法区分软件故障与人工越权。把“完成待办”放到事务之外则可能让其他审核员继续领取旧待办。对于演示中的单库模型四项变化一起提交是简单、可检查的做法。图 2状态、待办、历史与对外响应必须对应同一次裁决。响应不存入数据库事务但只能根据提交结果返回。“只有一个结果”也要限定范围。我们保证的是同一数据库、同一实例、同一资料版本下的一次审核决定。这不等于跨 CRM、合同系统、消息队列或 ERP 的全局 exactly once。一个成功审批后发送事件仍可能重复第 06 篇会通过 Outbox 与 Inbox 处理。一个远端动作发生了但本地没收到回复也不能用这里的revision自动判断它是否发生第 07 篇会处理未知结果。把这些范围讲清楚才不会把一条 SQL 条件更新误当成整个企业系统的可靠性答案。3. revision 是谁的版本material_version 又是谁的版本两个整数看起来相似含义却不同。workflow_instances.revision是流程实例记录的并发版本。只要会影响当前命令是否合法的流程修改发生例如审核完成、资料改版后待办替换它就应推进。客户端读取待办时同时获取这个值提交决定时带回。若值已经变化说明页面基于旧快照作出了判断服务端拒绝继续。它不是用户可自行填写的“审批轮次”也不是单纯的更新时间戳。applications.material_version是被审核材料的业务版本。审核员看到的是企业证照、联系人、套餐材料的某一版。即使流程实例恰好还是SUBMITTED旧资料上的同意也不能覆盖新资料。待办因此保存application_version审核请求要同时与申请当前版本比较。材料变更时旧待办进入SUPERSEDED新待办绑定新版本。这里把第 03 篇原有的OPEN|COMPLETED扩展为OPEN|COMPLETED|SUPERSEDED目的是保留旧任务的存在和失效原因不能直接删除后假装它从未出现。为什么不能只比较其中一个版本考虑主管在审核页面打开后销售补交了新材料流程服务只修改了申请材料版本却没有改变实例状态。若请求仅检查stateSUBMITTED主管可能用旧材料批准新申请。反过来若申请材料没有变另一个审核员已处理待办只比较材料版本也会让第二个决定通过。两种变化分别来自业务对象与过程对象校验也要覆盖两者。本示例的资料变更会同时推进实例revision这进一步使旧页面失效保留两项检查能把意图写清并避免未来代码改动时丢掉资料约束。图 3资料从 v1 升到 v2 后旧待办保留但标记失效新待办绑定 v2。旧页面的revision1, material_version1必须被拒绝。4. 为什么“先读再写”挡不住覆盖一个常见实现是先SELECT state, revision在 Python 中检查state SUBMITTED然后执行不带条件的UPDATE workflow_instances SET stateAPPROVED WHERE instance_id?。假设 A 和 B 都在检查后、写入前暂停两个请求就都获得了“可以写”的结论。后面的写入是彼此独立的第二次覆盖第一次。SELECT的检查本身没有把“读到 rev1”变成写入条件。最直接的修正是条件更新UPDATEworkflow_instancesSETstate:new_state,revisionrevision1,updated_atnow()WHEREinstance_id:instance_idANDstateSUBMITTEDANDrevision:expected_revisionRETURNINGrevision;两名审核员都传expected_revision1时第一笔成功更新把版本变成 2另一笔即使稍后获得写锁也不再匹配revision1RETURNING返回零行。应用把零行解释为冲突再读取当前实例帮助客户端刷新。PostgreSQL 官方文档说明UPDATE ... RETURNING只返回实际更新的行行级写锁会使竞争同一行的写入等待事务结束。因此条件必须留在UPDATE的WHERE中而不能只依赖请求前的应用层检查。PostgreSQL UPDATE、PostgreSQL 显式锁图 4右侧把旧版本写进数据库更新条件。并发等待解除后数据库重新判断条件败方匹配零行。这条 SQL 只是核心裁决语句。真实处理还要在同一事务里校验当前申请资料、当前待办、操作者的租户与权限以及写入待办完成和迁移历史。你不能把任意客户端传来的actor_id当作已认证身份它应从服务端认证上下文获得。tenant_id也不能仅用于页面筛选查询和修改必须受租户边界约束。本文离线脚本用一个字符串模拟操作者故只证明状态竞争逻辑不证明身份安全。若服务端要将冲突映射为 HTTP建议返回 409 和稳定错误码例如WORKFLOW_REVISION_CONFLICT让页面重新拉取详情参数格式错误才属于 400 一类。5. 为什么选择条件更新而不是把一切锁住PostgreSQL 可以用SELECT ... FOR UPDATE先锁定实例行校验相关记录后再更新也可以把预期版本放入UPDATE的条件。两者不是宗教选择。当前示例的决定很短、更新目标明确条件更新让冲突结果直接由“更新了几行”表达代码容易审查。若一个事务内必须依据多个相关行做复杂判定可以先对相关行按固定顺序加锁再保证它们与状态迁移一起提交。无论哪种方式都不能在持有数据库事务与锁时等待人工输入或发起耗时远端调用。数据库约束是第二道防线。本篇对每个实例只允许一个OPEN待办同时对同一资料版本的APPROVE/REJECT决策历史建立唯一索引。若未来有人绕过服务端流程直接写入数据库至少能拒绝一部分重复结果。唯一索引不能代替权限检查、状态迁移规则或历史的完整语义它只是对某项不可违反的业务不变量做持久约束。索引建在哪张表取决于哪些记录被定义为“有效决定”。补审、撤销或复核流程若被加入不能粗暴沿用“同一版本永远只有一条历史”而不重新定义业务规则。本篇可运行脚本选择 Python 标准库sqlite3以便读者不装数据库驱动就能看见竞态和回滚。SQLite 文档明确说明它一次只允许一个写入者因而脚本中的BEGIN IMMEDIATE会序列化写事务这与 PostgreSQL 的行级锁粒度不同。SQLite 隔离文档 本文不把 SQLite 实验冒充 PostgreSQL 压测它验证版本、待办和历史的业务裁决结果另给出 PostgreSQL 条件更新和事务 SQL 用于在第 03 篇的数据库上落地。并发吞吐、不同隔离级别下的行为和真实 HTTP 响应需要在实际部署的 PostgreSQL 与 API 中再测。6. 把条件写进可运行实验实验脚本位于 code/demo.py。它先创建applications、workflow_instances、tasks与transition_history四张教学表插入同一客户开通申请。两个线程在屏障处同时放行一个提交APPROVE另一个提交REJECT。每个线程使用独立数据库连接decide()打开写事务检查状态、实例版本、资料版本和待办绑定版本然后条件更新实例、完成待办、写历史。失败请求抛出Conflict事务回滚。请注意脚本没有“固定批准先赢”线程调度不同可能是驳回先提交。测试断言的是恰好一次成功、恰好一次冲突而不是人为指定哪位审核员赢。关键代码如下完整文件还包括表约束、资料改版和断言db.execute(BEGIN IMMEDIATE)rowdb.execute(SELECT w.state, w.revision, a.material_version, t.task_id, t.status, t.application_version FROM workflow_instances w JOIN applications a ON a.application_idw.application_id JOIN tasks t ON t.instance_idw.instance_id AND t.statusOPEN WHERE w.instance_id00000000-0000-4000-8000-000000000201 ).fetchone()ifrowisNoneorrow[0]!SUBMITTEDorrow[1]!expected_revision \orrow[2]!expected_material_versionorrow[5]!expected_material_version:raiseConflict(实例/资料/待办版本已变化请刷新后重审)changeddb.execute(UPDATE workflow_instances SET state?, revisionrevision1 WHERE instance_id00000000-0000-4000-8000-000000000201 AND stateSUBMITTED AND revision?,(new_state,expected_revision)).rowcountifchanged!1:raiseConflict(实例版本冲突)# 随后在同一事务内完成待办、写迁移历史再 commit。打开code目录运行python -X utf8 demo.py。一次真实执行的输出见 运行记录。由于线程调度不确定胜出结果可能显示APPROVED也可能是REJECTED两者都符合验收条件。固定不变的输出是成功数为 1、冲突数为 1、历史决策数为 1、待办完成数为 1以及实例版本由 1 变为 2。它并没有调用 PostgreSQL也没有启动 Web 服务实际验证范围在运行记录与 README 中写明。7. 把这个实验接回第 03 篇的应用第 03 篇已有POST /tasks/{id}/decision。接入时要让详情接口返回当前workflow_instances.revision与applications.material_version让提交接口接收客户端看到的版本。服务端从认证上下文读取操作者身份与租户先核对任务确属该租户与该申请再在一个 PostgreSQL 事务中完成条件更新、待办完成和历史写入。条件更新匹配零行时返回 409事务约束错误要回滚并转换为稳定错误码不能把数据库异常堆栈直接交给用户。若响应在提交后丢失客户端再次提交时仍可能收到 409客户端应查询最新状态和自己的原请求结果而不是把 409 自动理解为“第一次一定失败”。第 06 篇会加入请求幂等键使同一请求的重放语义更清晰。接入前还要执行数据库迁移。第 03 篇tasks.status的检查约束只允许OPEN|COMPLETED其表级约束也要求任务完成时必须有决定与操作者因此直接写入SUPERSEDED会失败。本篇提供 PostgreSQL 迁移脚本在原版第 03 篇 schema 上先替换tasks_status_check与tasks_check再建立“每实例一个开放待办”及“每资料版本一个审核决定”索引。我在隔离的 PostgreSQL 16 数据库上按第 03 篇原版 schema 顺序执行过该迁移约束与索引均创建成功。正式库运行前应先检查旧数据是否违反新增唯一性并安排可回滚的发布窗口。第 09 篇引入会签时同一实例可以有多个合法OPEN待办必须把这个阶段性索引改为按审批席位、资料版本等维度约束不能让本篇单人审批的索引阻断后续业务规则。资料修改入口同样要进入事务。它增加material_version推进流程revision使旧待办SUPERSEDED创建绑定新版本的待办。成功审批生成的后续事实应与批准版本关联。比如 v1 的合同签署在资料变成 v2 后不能自动满足 v2 的签署要求。到账是否继续有效取决于套餐和金额是否变化这个教学场景固定金额因此暂时保留到账事实。若真实业务允许改套餐或金额则要增加明确的款项对账规则不能照搬这条简化假设。图 5状态、实例版本和资料版本构成审批的最小条件身份、角色与租户控制属于 API 必须实现的额外门禁。8. 故障实验旧页面比并发更常见脚本的第二组实验先创建 v1 申请再让销售在审核前修改材料。修改后material_version2、实例revision2原00000000-0000-4000-8000-000000000301变为SUPERSEDED新00000000-0000-4000-8000-000000000302为OPEN。小周的旧页面仍传revision1, material_version1系统必须拒绝。刷新后的页面拿到新待办及版本才允许重新审核。这个场景没有两个线程却同样属于“基于旧快照做决定”。现实系统中审批人可能把浏览器页面开一整天材料改版比毫秒级线程竞态更容易遇到。我们还需要把冲突处理当成正常业务分支设计。失败请求不应悄悄改写对方的决定也不应无限重试直到覆盖成功。页面可以显示“申请内容已变化请查看新版材料”若已经由他人处理则显示当前处理人和结果保留用户原本点击的意图供界面提示但不能假装它生效。若客户对结果提出异议应启动明确的复核、撤销或重新申请流程而不是通过重复调用原审批接口篡改历史。流程工程追求的是结果可解释而不是所有按钮都必须返回成功。图 6验收至少覆盖相反决定并发、旧页面重放、材料改版和最终历史一致性。按图中的矩阵检查数据库竞态结束后transition_history有且仅有一条当前版本的审核决定tasks中只有一个已完成的胜出待办实例只推进一次。旧请求返回冲突时数据库状态与历史数量不变。资料改版后旧审批结果不能套用到 v2。数据库日志、应用日志和客户端错误码可以分别帮助排查问题但最终验收应以业务不变量为准。只看接口请求返回 200 或 409无法发现错误的后台副作用。9. 在 PostgreSQL 中安排事务的实际顺序把教学脚本搬到第 03 篇的 PostgreSQL 基座时应先决定锁住哪些记录再决定如何写条件语句。一种顺序是在事务内确认操作者与租户读取申请及待办确认待办仍然开放且绑定当前资料版本执行带revision的条件更新检查返回行数随后写待办和历史最后提交。若需要对申请行与实例行同时加锁所有写入路径都使用固定的锁顺序可以降低死锁概率。比如始终先处理申请再处理实例最后处理待办而不是审批接口先锁实例、材料修改接口先锁申请后相互等待。这些读取动作的时间应尽可能短。不要让审核界面打开时就在数据库保持事务等待审核员思考十分钟界面读取只是形成一个快照真正的事务从用户提交决定时才开始。不要在事务内调用 ERP、发送短信或等待队列确认。外部调用可能花几秒甚至几分钟锁会阻碍同一申请的其他合法变更也让数据库故障与外部故障纠缠。第 06 篇会把“需要通知下游”写成数据库内的 Outbox 记录事务提交后由独立中继发送。READ COMMITTED下的条件更新适合本例的单实例裁决但并不意味着“所有相关条件自动安全”。例如你在事务开始时读取了资料版本却没有阻止材料修改事务并发推进版本也没有把资料版本与审批决定的写入组织进同一套锁或版本规则那么审批仍可能对应到刚刚失效的材料。我们的离线脚本用 SQLite 的单写入者语义串行化了资料修改和审批在 PostgreSQL 落地时应使两条写入路径共同遵守申请行锁或可验证的条件更新协议。若采用SELECT ... FOR UPDATE要确认材料修改入口也获取相容的行锁只在审批入口加锁不足以约束另一个不合作的写入者。一个具体做法是在审批事务里先锁申请行读取material_version再锁或条件更新实例行材料修改事务也先锁申请行更新版本后推进实例版本、替换待办。这样两条路径不会在同一申请上互相穿插。如果团队使用更高隔离级别则需要理解序列化失败可能要求重试整个事务而不能只重复最后一条 SQL。重试时应重新读取材料与待办因为业务前提可能已经变化。这里的顺序是针对 AcmeFlow 的实施建议不是 PostgreSQL 替所有数据模型自动选择的通用模板。10. 冲突响应应该带什么不能带什么前端收到 409 后可以拿到稳定错误码、当前实例版本、当前资料版本和一个可重新拉取详情的链接。不要把另一个审核员的敏感备注、内部身份详情直接塞进所有人的错误响应展示范围仍受权限控制。客户端页面应保留用户刚填的文字提示“这份材料已经变化”引导其比较新版内容后重新决定。自动把原APPROVE重新套到最新版本会把并发控制化为形式检查虽然请求使用了新版本号人却从未看过新资料。若客户端在网络中丢失了成功响应第二次点击可能遇到同样的 409。此时不能简单展示“审批失败”因为第一次可能已经提交。服务端可让客户端查询待办是否由同一已认证操作者完成、历史中的决定是否与原命令一致更稳妥的方式是在第 06 篇引入请求幂等键保存该请求的结果以便重放时返回相同语义。这里的“相同语义”包括原本成功的决定而不是重新执行一次状态迁移。另一方面如果用不同键提交相反决定系统必须拒绝不能因为操作者相同就把反转当作重试。日志也要让排查者区分两种冲突一是revision不同说明实例已有变化二是资料版本不同说明审核依据过期。对用户它们都可以是 409但服务端应记录可检索的原因码、申请 ID、租户 ID、待办 ID、客户端看到的版本和数据库当前版本。日志中不需要拷贝完整营业执照或客户敏感资料。这样运营人员说“我明明点了通过却没生效”时维护者能用时间线解释谁先提交、哪份资料被审核、旧请求为什么失效。11. 单人审批与会签的边界本篇“同一版本只有一个有效决定”适用于一个运营审核任务由一位审核员完成的规则。后续第 09 篇会讨论会签财务、法务和运营可能都要分别表达意见那时多个决定本来就是业务要求。系统需要以“任务角色 审批轮次 资料版本”定义唯一性并规定何时汇合而不是沿用一条只允许一个人决定的索引。即使在会签中每个具体待办仍应有自己的并发约束同一个法务待办不能被两位法务人员同时完成为相反结果。“先到先得”也是本篇的一项业务选择而不是所有审批流程的自然法则。有的组织要求主管决策优先有的要求多人协商有的要求任何驳回都终止有的允许被驳回后补件重审。数据库只能忠实执行已定义的规则不能替团队决定权限等级与裁决顺序。若规则改变应在命令处理前明确谁能处理、谁有否决权、什么状态可重开并记录规则版本。并发控制把规则执行得稳定规则本身仍来自业务设计。最后别把“冲突计数为零”当作优化目标。真实使用中冲突偶尔出现很正常尤其在热门待办列表或同一客户频繁改材料时。异常的是冲突被静默覆盖、没有可理解的提示或者每次冲突都诱发无限自动重试。可以监控冲突率帮助发现界面刷新太慢、待办分配不合理但首先要确保冲突处理正确。对最终用户而言一个明确的“已有更新请重新查看”比一个看似成功却修改了错误版本的审批可靠得多。复现实验时还要记得检查数据文件而不仅是控制台输出。脚本运行后会清理临时 SQLite 文件适合做快速回归若要逐行观察事务可以把临时目录改为固定路径并用数据库工具查询四张表。线上验证则应使用隔离的测试租户和可清理的申请覆盖多进程、多连接与实际反向代理下的请求重放。示例脚本的两条线程和本地文件系统只能说明这段代码的裁决规则可运行不能替代部署环境的并发测试。尤其要分别测试“两个不同决定”和“两个相同决定”。前者要求一个胜出、一个冲突后者在未引入请求幂等键前也可能是一个成功、一个冲突。不能因为两个审核员都点了通过就把第二次写入也算成成功决定否则历史与通知仍可能重复。第 06 篇为同一调用的重放增加请求键之后来自同一调用的第二次请求可以返回原结果而两个独立审核员的相同选择仍是两个不同命令。这个细微区别影响界面提示也影响审计对“谁作出决定”的解释。还应测试事务中途失败。例如实例状态已在事务内改成APPROVED写历史前数据库报错回滚后应仍为SUBMITTED待办继续开放历史也不增加。如果回滚后只有其中一项被保留说明部分写入使用了另一个连接、隐式自动提交或数据库之外的副作用。这个故障实验比单纯跑一百次线程竞赛更能验证事务边界。业务人员不需要理解锁的名字但他们需要相信“系统显示通过”与“待办完成、时间线留下决定”总是同一个结果。最后检查租户隔离与对象关联。在测试库里另建一个租户也使用CUSTOMER-001作为业务键是合法的因为唯一性范围是租户内但任一审批请求只能触达所属租户的申请、实例和待办。若 API 先按task_id找任务再没有核对申请租户就可能把正确的并发控制用于错误的客户资料。并发安全解决不了越权访问两个约束必须一起写入服务端查询条件和验收用例。本文脚本只演示单个教学租户相关安全测试属于接入 API 后的必要工作。12. Workflow Thinking冲突不等于故障许多工程团队把 409 当成异常接入统一重试中间件之后旧审批请求被自动重试三次。这样既不会让旧决定变得合法还会增加数据库压力和误导性告警。版本冲突通常意味着业务事实已经改变另一位审核员先处理了任务或者销售修改了资料。下一步是重新读取与重新判断而不是盲目重发原命令。只有可安全重试的暂时性技术故障才进入第 07 篇的退避策略。当团队讨论“是否需要上工作流引擎”时也别把并发控制视为引擎会自动替你解决的全部问题。无论使用代码驱动的工作流框架、BPMN 引擎还是自建状态机外部请求仍需要携带业务对象版本人工决定仍须绑定被审核资料服务端仍须执行授权检查。引擎可以帮助编排任务和持久等待却无法知道一份过期营业执照能否通过审核。把这类业务判断明确写入数据和命令契约才是后续接入其他引擎时最容易复用的部分。本篇的最终结论很具体对同一版本资料的审批数据库只接受一个当前状态下的决定输掉竞态的请求返回冲突要求重新读取。第 06 篇沿着“成功审批之后要通知别的系统”继续推进处理状态已经提交、通知却可能在网络中丢失或重复的问题。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Shell正则表达式实战:核心语法与grep/sed/awk高频用法 2026/9/28 5:43:57

Shell正则表达式实战:核心语法与grep/sed/awk高频用法

写Shell脚本这些年,我越来越觉得正则表达式就是Shell的“文本手术刀”。很多人一提到正则就头大,觉得符号太多记不住,实际上只要抓住几个核心概念,再加上在grep、sed、awk里的实际用法,你就能解决日常开发运维里九成以…

阅读更多 →
数字孪生云渲染选型实操:从三层架构到压测验收的完整指南 2026/9/28 5:43:57

数字孪生云渲染选型实操:从三层架构到压测验收的完整指南

数字孪生和虚拟仿真项目这几年明显变多,但我被问得最多的不是建模怎么做,而是云渲染服务商到底该怎么选。很多人以为选一个“能出效果图”的公司就行,结果项目做到一半发现画面推不动、并发上不去、信创环境不兼容,甚至连数据接入…

阅读更多 →
数字孪生云渲染服务商选型指南:从需求梳理到POC测试的避坑实战 2026/9/28 5:43:57

数字孪生云渲染服务商选型指南:从需求梳理到POC测试的避坑实战

做数字孪生项目这些年,我见过太多团队在服务商选型这一步栽跟头。项目立项时PPT做得漂亮,开会时需求讲得头头是道,可真到选型那一步,面对一堆术语——数字孪生体、实时云渲染、信创适配、并发量、POC测试——不少人直接就懵了。有…

阅读更多 →
鸿蒙混合开发实战:Flutter公告模块架构与优化全解析 2026/9/28 5:43:57

鸿蒙混合开发实战:Flutter公告模块架构与优化全解析

1. 为什么"享家社区"最终选择了Flutter做鸿蒙公告模块1.1 项目场景与核心需求拆解"享家社区"是一个面向智慧社区场景的APP,业主需要在这里查收物业公告、社区活动通知、停水停电提醒、业委会决议公示等。公告管理功能听起来简单,实际…

阅读更多 →
Git远程仓库第一次推送:强制推送与初始化选项详解 2026/9/28 5:43:56

Git远程仓库第一次推送:强制推送与初始化选项详解

1. 强推之前,先搞清楚Git在怕什么:远程空仓库与本地历史的第一次握手很多人第一次接触"推送本地新建项目到Gitee或GitHub"时,都会遇到一个极其常见的报错:failed to push some refs,或者是Updates were reje…

阅读更多 →
给 Amp 配置自定义 API:CLIProxyAPI 接入教程(TaoToken 统一 Key 版) 2026/9/28 5:43:50

给 Amp 配置自定义 API:CLIProxyAPI 接入教程(TaoToken 统一 Key 版)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉