material_manage_system/internal/repository/update.go
文睿 张 696e90893b fix: 主数据编辑的两类静默失败
UPDATE 漏列:
- code_rules 的 SET 补上 category_id。此前改分类提示"保存成功"但不落库,
  是唯一用户可达的活缺陷(同页的分类下拉没有 disabled,且规则只能改不能删,
  没有"删了重建"这条退路)
- code_rules 有 uk_code_rules_org_category,补列后改到已占用的分类会撞唯一键,
  新建与更新统一翻成中文的 ErrDuplicateCodeRule
- 分类/单位/仓库/库位/伙伴的编码定为不可变:分类编码是编码规则前缀的来源
  (init.sql 的种子数据直接取 material_categories.code),伙伴编码是对外业务
  编号,改掉会让历史数据脱节且查不回来。service 的 Update* 不再要求编码非空
  ——原先是"必须传、传了却被丢弃";新建侧的必填校验保留
- 顺带补齐两处同款漏网:organization.go 的 code 与 partner.go 的 partner_code
  (前者 service 本就不校验编码,自洽,无需改)

记录不存在时的静默成功:
- DSN 开启 clientFoundRows,让 RowsAffected 返回"匹配"行数而非"实际改变"行数。
  否则"编辑时一个字段都没改"和"记录不存在"都返回 0,无法区分,直接加检查会把
  正常保存误判成记录不存在(真 MySQL 8.0 实测)
- 新增 repository.execExpectingRow,7 个 Update* 影响 0 行时返回 ErrRecordNotFound;
  伙伴走事务内的 SELECT ... FOR UPDATE,只需翻译 sql.ErrNoRows
- 既有 4 处 RowsAffected 用法逐处核对过,均不受该 flag 影响

测试:补 6 个 Update* 的存在性检查、编码必填约束的两端、唯一键翻译、DSN 拼接;
全部做过变异验证(抽掉实现后对应测试确实变红)。
2026-09-21 17:26:53 +08:00

33 lines
1.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"errors"
"github.com/wmms/wmms/pkg/database"
)
// ErrRecordNotFound 表示按 id + 组织没查到目标记录:要么 id 不存在,要么这条记录
// 属于别的组织。按主键更新时影响 0 行就返回它,避免「改了一条不存在的记录」还提示
// 保存成功。
var ErrRecordNotFound = errors.New("记录不存在,可能已被删除或不属于当前组织")
// execExpectingRow 执行一条按主键更新的语句,并要求它至少匹配到一行。
//
// 判断依据是 RowsAffected,而 MySQL 默认返回的是「实际改变」的行数——编辑弹窗里
// 一个字段都没动时同样是 0,跟记录不存在分不开。所以 DSN 必须开 clientFoundRows=true
// (见 pkg/database.Init)把语义换成「匹配」的行数,这个检查才成立。
func execExpectingRow(query string, args ...interface{}) error {
result, err := database.DB.Exec(query, args...)
if err != nil {
return err
}
affected, err := result.RowsAffected()
if err != nil {
return err
}
if affected == 0 {
return ErrRecordNotFound
}
return nil
}