Merge remote-tracking branch 'origin/main' into codex/mkh-alert-export-0917
origin/main 已前进 11 个提交(PR #11~#15),其中 #11「配件仓位只取箱及仓位库存表」 与 #12「从模块化成品列表排除配件档案」改动了与本分支相同的三个文件。 冲突处理(3 个文件,逐处保留双方意图,未使用 ours/theirs 批量取舍): - backend/src/services/mkhBomService.js(4 处):取本分支侧。本分支的 ⑤~⑪ 注释与 loadComponentLocations 是 origin/main 那段的超集,且双方都已删除 buildInventoryWarehouseMap 回退链,语义一致;同时并入 origin/main 收紧后的 MKH_WHERE(必须是 jst_skuBomItem 正式父项)与文件头 ① 的新口径说明。 - backend/test/mkhBomService.test.js(1 处):hunk 边界冲突,双方各缺一个 `});`。 origin/main 新增的「无箱记录不回退仓库」测试按语义插在仓位测试之后, 再接本分支新增的测试,两处闭合各自补回。 - frontend/src/pages/MkhBomData/index.jsx(2 处):合并双方文案。数据口径采用 origin/main 的表述(正式 BOM 父项、仓位只取 rpt.jst_packInventoryInfo), 并保留本分支的补货预警 / 待发货订单 / 在途加工单 / 配件不足四段说明。 顺带校正 mkhBomService.js 注释中因成品口径收紧而过期的数字 (成品总数 4408 → 见 ①,预警数 26 → 25),仅改注释,不动逻辑。 合并后重新验证:后端语法检查 151 个文件通过;mkhBomService + mkhReplenishAlertJob 单测 52/52 通过;接口实测 total 4223 / warningCount 25 / componentShortageCount 10。
This commit is contained in:
commit
06952d8a04
@ -6,7 +6,7 @@
|
|||||||
- 保留他人的未提交修改和已有提交,仅暂存本任务内容;不得清理或覆盖未知工作。
|
- 保留他人的未提交修改和已有提交,仅暂存本任务内容;不得清理或覆盖未知工作。
|
||||||
- 推送前同步远端,逐项检查最终差异并验证;被拒绝后先查明并合并更新,禁止任何形式的强推。
|
- 推送前同步远端,逐项检查最终差异并验证;被拒绝后先查明并合并更新,禁止任何形式的强推。
|
||||||
- 不改写已发布历史,不批量选择冲突的某一方;不明确的行为取舍需要负责人确认。
|
- 不改写已发布历史,不批量选择冲突的某一方;不明确的行为取舍需要负责人确认。
|
||||||
- 所有变更通过 PR 进入 `main`,不得直接推送主分支;独立审核与最新内容的验证通过后由维护者合并。
|
- 所有变更通过 PR 进入 `main`,不得直接推送主分支;默认由非提交者审核。仓库所有者或管理员明确授权某个 PR 合并时,该书面授权可以代替独立审核,但仍必须同步最新主分支、验证最终提交并保留授权记录。
|
||||||
- 不提交凭据、私钥、环境秘密或真实业务数据;不把合并授权当成生产部署授权。
|
- 不提交凭据、私钥、环境秘密或真实业务数据;不把合并授权当成生产部署授权。
|
||||||
- 新建或修改数据表、映射表、查询和同步任务时,必须按实际过滤、关联、排序和唯一性设计索引,用执行计划和有代表性数据验证负载;禁止无边界全表扫描、无节制并发和重复全量同步。
|
- 新建或修改数据表、映射表、查询和同步任务时,必须按实际过滤、关联、排序和唯一性设计索引,用执行计划和有代表性数据验证负载;禁止无边界全表扫描、无节制并发和重复全量同步。
|
||||||
- 开发机器使用中央系统数据库和公网 TLS Redis,无需安装本地数据库;生产部署必须先将代码通过 PR 合入 `main`,并在 `/opt/cangku/deploy.lock` 锁内从服务器 Git 工作区快进拉取和验证。
|
- 开发机器使用中央系统数据库和公网 TLS Redis,无需安装本地数据库;生产部署必须先将代码通过 PR 合入 `main`,并在 `/opt/cangku/deploy.lock` 锁内从服务器 Git 工作区快进拉取和验证。
|
||||||
|
|||||||
@ -8,7 +8,10 @@
|
|||||||
//
|
//
|
||||||
// ① MKH 商品编码(列表主数据)—— 业务库 JSReportReplica(MSSQL / schema=rpt)
|
// ① MKH 商品编码(列表主数据)—— 业务库 JSReportReplica(MSSQL / schema=rpt)
|
||||||
// rpt.jst_sku WHERE sku_id LIKE N'MKH%' AND item_type = N'成品'
|
// rpt.jst_sku WHERE sku_id LIKE N'MKH%' AND item_type = N'成品'
|
||||||
// AND other_3 = N'简尚-崔桥工厂品牌仓' (当前 4408 条)
|
// AND other_3 = N'简尚-崔桥工厂品牌仓'
|
||||||
|
// AND 该编码存在正式 BOM 配件明细 (当前 4223 条)
|
||||||
|
// · 商品档案里的 item_type/labels 不能单独作为成品依据:上游也会把部分配件
|
||||||
|
// 标成“成品/模块化成品”。只有 jst_skuBomItem 的父项才进入本页面。
|
||||||
// · 形态 MKH + 款号 + 序列,例 MKHDNZ0409260600198
|
// · 形态 MKH + 款号 + 序列,例 MKHDNZ0409260600198
|
||||||
//
|
//
|
||||||
// ② 配件明细(BOM)—— 聚水潭商品 BOM(主来源)
|
// ② 配件明细(BOM)—— 聚水潭商品 BOM(主来源)
|
||||||
@ -65,13 +68,15 @@
|
|||||||
// · 近7日日销 rpt.pm_stockMonitor.sevenAvgSale
|
// · 近7日日销 rpt.pm_stockMonitor.sevenAvgSale
|
||||||
// (warehouse = '简尚-崔桥工厂品牌仓',按 date 取最新一条快照)
|
// (warehouse = '简尚-崔桥工厂品牌仓',按 date 取最新一条快照)
|
||||||
// · 现有库存 rpt.jst_inventoryInfo(wms_co_id = 15870495 品牌仓)实时库存;
|
// · 现有库存 rpt.jst_inventoryInfo(wms_co_id = 15870495 品牌仓)实时库存;
|
||||||
// 该仓对 MKH 成品只覆盖约 256 / 4408 条,缺失时回退监控快照 jsStock
|
// 该仓对 MKH 成品只覆盖约 256 / 4408 条(2026-09-16 快照;成品总数口径见 ①),
|
||||||
|
// 缺失时回退监控快照 jsStock
|
||||||
// · 可用天数 = 现有库存 / 近7日日销
|
// · 可用天数 = 现有库存 / 近7日日销
|
||||||
// · 建议补货量 = ceil(3 × 近7日日销 − 现有库存),不足 0 取 0
|
// · 建议补货量 = ceil(3 × 近7日日销 − 现有库存),不足 0 取 0
|
||||||
// ⚠️ 刻意**不用**源系统的 jsUseDays 列:实测它与 jsStock / sevenAvgSale 不同源
|
// ⚠️ 刻意**不用**源系统的 jsUseDays 列:实测它与 jsStock / sevenAvgSale 不同源
|
||||||
// (同一 SKU 可差 20 天以上,如 MKHDNZ0409260700488 库存 44 / 日销 0.67,
|
// (同一 SKU 可差 20 天以上,如 MKHDNZ0409260700488 库存 44 / 日销 0.67,
|
||||||
// 除法 65.7 天而 jsUseDays 为 44),本页口径以业务给定的「近7日日销」为准。
|
// 除法 65.7 天而 jsUseDays 为 44),本页口径以业务给定的「近7日日销」为准。
|
||||||
// 实测(2026-09-16 快照):4408 个 MKH 成品中 114 个有日销、13 个可用天数 < 3 天。
|
// 实测(2026-09-16 快照,当时成品口径尚未收紧为「正式 BOM 父项」):
|
||||||
|
// 4408 个 MKH 成品中 114 个有日销、13 个可用天数 < 3 天。
|
||||||
//
|
//
|
||||||
// ── ⑧ 在途加工单(2026-09-17 业务确认口径)────────────────────────
|
// ── ⑧ 在途加工单(2026-09-17 业务确认口径)────────────────────────
|
||||||
// 规则:**只要该 MKH 商品存在未完成的加工单,就不再列入补货预警**
|
// 规则:**只要该 MKH 商品存在未完成的加工单,就不再列入补货预警**
|
||||||
@ -98,7 +103,7 @@
|
|||||||
// 官方 BOM 明细按 SKU 分批读取(单批上限 MAX_PAGE_SIZE=200,见 loadOfficialBomForSkuBatches),
|
// 官方 BOM 明细按 SKU 分批读取(单批上限 MAX_PAGE_SIZE=200,见 loadOfficialBomForSkuBatches),
|
||||||
// 只对筛选结果取数,因此「仅预警」这类小范围导出只需 1 批查询;
|
// 只对筛选结果取数,因此「仅预警」这类小范围导出只需 1 批查询;
|
||||||
// 每批结果都进 officialBomDetailCache,重复导出直接命中缓存。
|
// 每批结果都进 officialBomDetailCache,重复导出直接命中缓存。
|
||||||
// ⚠️ products 形态**不返回 components** —— 4408 行商品若都挂上配件明细,
|
// ⚠️ products 形态**不返回 components** —— 全量商品(4000+ 行)若都挂上配件明细,
|
||||||
// 响应会膨胀到几十 MB,而商品清单本身用不到明细。
|
// 响应会膨胀到几十 MB,而商品清单本身用不到明细。
|
||||||
//
|
//
|
||||||
// ── ⑩ 待发货订单(2026-09-17 业务确认口径)────────────────────────
|
// ── ⑩ 待发货订单(2026-09-17 业务确认口径)────────────────────────
|
||||||
@ -147,7 +152,7 @@
|
|||||||
// · 判定范围:**缺货 + 预警都判**(业务确认),不改变原有预警级别与排序,
|
// · 判定范围:**缺货 + 预警都判**(业务确认),不改变原有预警级别与排序,
|
||||||
// 只额外打标记;「已排产」商品本来就不预警,不参与判定。
|
// 只额外打标记;「已排产」商品本来就不预警,不参与判定。
|
||||||
// · 无 BOM 配件明细的商品 → 可组装数未知(组件缺失 ≠ 配件不足),不做判定。
|
// · 无 BOM 配件明细的商品 → 可组装数未知(组件缺失 ≠ 配件不足),不做判定。
|
||||||
// 实测(2026-09-17):预警 26 个(缺货 9 + 预警 17)中 **10 个可组装数 = 0**
|
// 实测(2026-09-17,并入最新 main 后复核):预警 25 个(缺货 9 + 预警 16)中 **10 个可组装数 = 0**
|
||||||
// (缺货 6 + 预警 4);扣加工单占用前后仅 1 个商品变化(185 → 180 台),
|
// (缺货 6 + 预警 4);扣加工单占用前后仅 1 个商品变化(185 → 180 台),
|
||||||
// 说明占用规模小但口径上必须扣。
|
// 说明占用规模小但口径上必须扣。
|
||||||
// ⚠️ 配件库存查询失败时**不做任何判定**(宁可少标也不误标),
|
// ⚠️ 配件库存查询失败时**不做任何判定**(宁可少标也不误标),
|
||||||
@ -171,9 +176,18 @@ const officialBomDetailCache = new Map();
|
|||||||
const componentLocationCache = new Map();
|
const componentLocationCache = new Map();
|
||||||
let productBomCache = null;
|
let productBomCache = null;
|
||||||
|
|
||||||
// MKH 商品编码 = 模块化成品
|
// MKH 模块化成品必须同时是指定仓库商品档案和正式 BOM 父项。
|
||||||
|
// 上游存在被 item_type/labels 误标为成品的配件,不能仅依赖商品档案标签。
|
||||||
const DESIGNATED_WAREHOUSE = '简尚-崔桥工厂品牌仓';
|
const DESIGNATED_WAREHOUSE = '简尚-崔桥工厂品牌仓';
|
||||||
const MKH_WHERE = `s.sku_id LIKE N'MKH%' AND s.item_type = N'成品' AND s.other_3 = N'${DESIGNATED_WAREHOUSE}'`;
|
const MKH_WHERE = `s.sku_id LIKE N'MKH%'
|
||||||
|
AND s.item_type = N'成品'
|
||||||
|
AND s.other_3 = N'${DESIGNATED_WAREHOUSE}'
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM rpt.jst_skuBomItem parent_bom WITH (NOLOCK)
|
||||||
|
WHERE parent_bom.sku_id = s.sku_id
|
||||||
|
AND NULLIF(LTRIM(RTRIM(parent_bom.map_outer_sku_id)), N'') IS NOT NULL
|
||||||
|
)`;
|
||||||
|
|
||||||
// ── 补货预警参数(见文件头 ⑦)────────────────────────────────────
|
// ── 补货预警参数(见文件头 ⑦)────────────────────────────────────
|
||||||
// 品牌仓 wms_co_id(rpt.jst_warehouseInfo 查得)
|
// 品牌仓 wms_co_id(rpt.jst_warehouseInfo 查得)
|
||||||
|
|||||||
@ -155,14 +155,19 @@ test('pack inventory locations match a BOM component and its package suffixes',
|
|||||||
test('pack inventory location prefers bins and falls back to positive-stock box ids', () => {
|
test('pack inventory location prefers bins and falls back to positive-stock box ids', () => {
|
||||||
const locations = _private.buildPackLocationMap([
|
const locations = _private.buildPackLocationMap([
|
||||||
{
|
{
|
||||||
skuId: 'PART-1-1', warehouseId: 1, warehouseName: '半成品仓', binGroup: '成品区', bin: 'A-01', packId: 'BOX-1', qty: 2,
|
skuId: 'PART-1-1', warehouseId: 1, binGroup: '成品区', bin: 'A-01', packId: 'BOX-1', qty: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
skuId: 'PART-1-2', warehouseId: 1, binGroup: null, bin: '', packId: 'BOX-2', qty: 1,
|
skuId: 'PART-1-2', warehouseId: 1, binGroup: null, bin: '', packId: 'BOX-2', qty: 1,
|
||||||
},
|
},
|
||||||
], ['PART-1']);
|
], ['PART-1']);
|
||||||
|
|
||||||
assert.equal(locations.get('PART-1'), '半成品仓 · 成品区 / A-01、仓库 1 · 箱号 BOX-2');
|
assert.equal(locations.get('PART-1'), '仓库 1 · 成品区 / A-01、仓库 1 · 箱号 BOX-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('components without pack inventory rows do not receive a fallback warehouse location', () => {
|
||||||
|
const locations = _private.buildPackLocationMap([], ['PART-1']);
|
||||||
|
assert.equal(locations.has('PART-1'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── 配件仓位:只认「箱及仓位库存」,不做库存表/商品档案仓回退(2026-09-17 业务口径)──
|
// ── 配件仓位:只认「箱及仓位库存」,不做库存表/商品档案仓回退(2026-09-17 业务口径)──
|
||||||
|
|||||||
43
deploy/production/build-frontend.sh
Executable file
43
deploy/production/build-frontend.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
FRONTEND_DIR="$REPO_DIR/frontend"
|
||||||
|
SOURCE_GIT_FULL_SHA="$(cd "$REPO_DIR" && git rev-parse HEAD)"
|
||||||
|
SOURCE_GIT_SHA="$(cd "$REPO_DIR" && git rev-parse --short HEAD)"
|
||||||
|
SOURCE_GIT_REF="$(cd "$REPO_DIR" && git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
||||||
|
SOURCE_GIT_REF="${SOURCE_GIT_REF:-main}"
|
||||||
|
|
||||||
|
test -f "$FRONTEND_DIR/package-lock.json" || {
|
||||||
|
echo 'frontend/package-lock.json 不存在,停止构建' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-e "SOURCE_GIT_SHA=$SOURCE_GIT_SHA" \
|
||||||
|
-e "SOURCE_GIT_FULL_SHA=$SOURCE_GIT_FULL_SHA" \
|
||||||
|
-e "SOURCE_GIT_REF=$SOURCE_GIT_REF" \
|
||||||
|
-v "$FRONTEND_DIR:/host-frontend" \
|
||||||
|
node:22-bookworm-slim \
|
||||||
|
sh -lc '
|
||||||
|
set -eu
|
||||||
|
mkdir -p /workspace
|
||||||
|
tar -C /host-frontend --exclude=node_modules --exclude=dist -cf - . | tar -C /workspace -xf -
|
||||||
|
cd /workspace
|
||||||
|
npm ci --no-audit --no-fund
|
||||||
|
npm run build
|
||||||
|
mkdir -p /host-frontend/dist
|
||||||
|
find /host-frontend/dist -mindepth 1 -delete
|
||||||
|
cp -a /workspace/dist/. /host-frontend/dist/
|
||||||
|
'
|
||||||
|
|
||||||
|
node - "$FRONTEND_DIR/dist/__build_version__.json" "$SOURCE_GIT_FULL_SHA" <<'NODE'
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const [versionPath, expectedSha] = process.argv.slice(2);
|
||||||
|
const metadata = JSON.parse(fs.readFileSync(versionPath, 'utf8'));
|
||||||
|
if (metadata.sourceGitFullSha !== expectedSha) {
|
||||||
|
throw new Error(`前端构建 SHA 不匹配:${metadata.sourceGitFullSha} != ${expectedSha}`);
|
||||||
|
}
|
||||||
|
process.stdout.write(`前端构建已验证:${metadata.version}\n`);
|
||||||
|
NODE
|
||||||
1042
frontend/package-lock.json
generated
1042
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -37,9 +37,6 @@
|
|||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.24",
|
"eslint-plugin-react-refresh": "^0.4.24",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
"vite": "npm:rolldown-vite@7.2.5"
|
"vite": "7.2.7"
|
||||||
},
|
|
||||||
"overrides": {
|
|
||||||
"vite": "npm:rolldown-vite@7.2.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -700,7 +700,7 @@ const MkhBomData = () => {
|
|||||||
dataIndex: 'location',
|
dataIndex: 'location',
|
||||||
width: 140,
|
width: 140,
|
||||||
render: (v) => (v ? text(v) : (
|
render: (v) => (v ? text(v) : (
|
||||||
<Tooltip title="上游未回传库位数据">
|
<Tooltip title="箱及仓位库存表中没有该配件的有效记录">
|
||||||
<span className="mkh-muted">未维护</span>
|
<span className="mkh-muted">未维护</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)),
|
)),
|
||||||
@ -720,10 +720,10 @@ const MkhBomData = () => {
|
|||||||
模块化(MKH 开头)成品的配件明细与补货预警:近 7 日日销核算,库存低于 3 天日销即预警
|
模块化(MKH 开头)成品的配件明细与补货预警:近 7 日日销核算,库存低于 3 天日销即预警
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="secondary" className="mkh-note">
|
<Text type="secondary" className="mkh-note">
|
||||||
数据口径:MKH 商品编码仅取自业务库 jst_sku 中“简尚-崔桥工厂品牌仓”的成品(当前 4408 条);
|
数据口径:MKH 商品编码取自业务库 jst_sku 中“简尚-崔桥工厂品牌仓”,并且必须是聚水潭正式 BOM 的父项;
|
||||||
配件明细与数量优先取自聚水潭商品 BOM,业务中台组合商品 BOM 与产品资料系统数据库中维护的配件作为补充。
|
配件明细与数量优先取自聚水潭商品 BOM,业务中台组合商品 BOM 与产品资料系统数据库中维护的配件作为补充;
|
||||||
无指定仓库商品档案的 BOM 组合编码不会进入列表;配件仓位只取聚水潭「箱及仓位库存」,
|
无指定仓库商品档案的 BOM 组合编码不会进入列表;配件仓位只取 rpt.jst_packInventoryInfo,
|
||||||
有维护货位则显示货位、未维护货位则显示箱号,该表没有记录即显示「未维护」。
|
优先显示具体仓位,没有具体仓位时显示箱号,表中没有有效记录时显示「未维护」。
|
||||||
<br />
|
<br />
|
||||||
补货预警:近7日日销取聚水潭库存监控快照(监控日 {data.monitorDate || '—'}),现有库存优先取品牌仓实时库存、
|
补货预警:近7日日销取聚水潭库存监控快照(监控日 {data.monitorDate || '—'}),现有库存优先取品牌仓实时库存、
|
||||||
该仓无记录时回退监控快照库存;可用天数 = 现有库存 ÷ 近7日日销,低于 3 天即预警,缺货单独标识;
|
该仓无记录时回退监控快照库存;可用天数 = 现有库存 ÷ 近7日日销,低于 3 天即预警,缺货单独标识;
|
||||||
@ -861,7 +861,7 @@ const MkhBomData = () => {
|
|||||||
: '聚水潭商品 BOM 及补充来源中尚无可用的模块化商品配件明细'}
|
: '聚水潭商品 BOM 及补充来源中尚无可用的模块化商品配件明细'}
|
||||||
description={(
|
description={(
|
||||||
<span>
|
<span>
|
||||||
MKH 商品编码({data.total || 0} 条)仅取自业务库 jst_sku 中“简尚-崔桥工厂品牌仓”;
|
MKH 商品编码({data.total || 0} 条)取自业务库 jst_sku 中“简尚-崔桥工厂品牌仓”的正式 BOM 父项;
|
||||||
配件明细优先取自聚水潭商品 BOM({data.bomOfficialRowCount || 0} 个),
|
配件明细优先取自聚水潭商品 BOM({data.bomOfficialRowCount || 0} 个),
|
||||||
业务组合 BOM({data.bomBusinessRowCount || 0} 个)与产品资料系统({data.bomProductDbRowCount || 0} 个)作为补充。
|
业务组合 BOM({data.bomBusinessRowCount || 0} 个)与产品资料系统({data.bomProductDbRowCount || 0} 个)作为补充。
|
||||||
BOM 中没有指定仓库商品档案的组合编码不会加入商品列表。
|
BOM 中没有指定仓库商品档案的组合编码不会加入商品列表。
|
||||||
|
|||||||
11
仓库提交与合并规范.md
11
仓库提交与合并规范.md
@ -168,12 +168,12 @@ npm --prefix frontend run check
|
|||||||
|
|
||||||
合并要求:
|
合并要求:
|
||||||
|
|
||||||
1. 至少一名非提交者的维护者或开发者审核最终差异;AI 自我复查可以辅助,但不代替独立审核。提交 AI 不得用另一个会话或共享账号冒充独立审核者。
|
1. 默认由至少一名非提交者的维护者或开发者审核最终差异;AI 自我复查可以辅助,但不代替独立审核。仓库所有者或管理员对明确的 PR 作出书面合并指令时,该授权可以代替独立审核,但必须在 PR 中记录授权人、授权范围和当时的最终提交 SHA。提交 AI 不得伪造授权、冒用其他账号或把模糊的日常开发指令解释为合并授权。
|
||||||
2. 审核需关注是否撤销他人逻辑、丢失配置/依赖、改变接口与权限、破坏已上线功能,不能只看有无冲突或测试绿灯。
|
2. 审核需关注是否撤销他人逻辑、丢失配置/依赖、改变接口与权限、破坏已上线功能,不能只看有无冲突或测试绿灯。
|
||||||
3. 审核和检查必须对应最终待合并提交;后续新提交或冲突解决后需要重新确认,不能沿用失效的批准。
|
3. 审核、所有者/管理员授权和检查必须对应最终待合并提交;后续新增业务代码、依赖变更或冲突解决后需要重新确认,不能沿用失效的批准。授权后仅补充不改变行为的合并记录或文档时,应在 PR 中说明并重新检查最终差异。
|
||||||
4. 合并前再次确认最新 `main` 已被纳入任务分支,并验证组合后的行为。其他 PR 先合并导致主分支前进时,更新本 PR、重新检查并确认审核后再合并。
|
4. 合并前再次确认最新 `main` 已被纳入任务分支,并验证组合后的行为。其他 PR 先合并导致主分支前进时,更新本 PR、重新检查并确认审核或所有者/管理员授权仍适用于最终提交后再合并。
|
||||||
5. 由维护者通过仓库 PR 合并功能依次处理;同时有多个待合并 PR 时串行合并。有合并队列时使用针对最新组合提交的检查,避免两份旧验证同时放行。
|
5. 由维护者通过仓库 PR 合并功能依次处理;同时有多个待合并 PR 时串行合并。有合并队列时使用针对最新组合提交的检查,避免两份旧验证同时放行。
|
||||||
6. 未解决冲突、必需检查失败、尚未审核或同步状态不清楚时不得合并。缺少独立审核者时保持 PR 待审核,不自行批准。
|
6. 未解决冲突、必需检查失败、同步状态不清楚,或既无独立审核也无所有者/管理员明确授权时不得合并。授权例外不允许绕过失败检查、未解决冲突、数据库风险证据或分支保护,也不允许直接推送 `main`。
|
||||||
7. 合并后核对主分支结果。删除任务分支前确认已合并且无人继续使用,不删除其他开发者的分支。
|
7. 合并后核对主分支结果。删除任务分支前确认已合并且无人继续使用,不删除其他开发者的分支。
|
||||||
|
|
||||||
## 7. 发现误合并或代码被覆盖
|
## 7. 发现误合并或代码被覆盖
|
||||||
@ -190,6 +190,7 @@ npm --prefix frontend run check
|
|||||||
- [ ] 涉及数据表、索引、映射、查询或同步时,已验证执行计划、数据量边界和负载控制,并在 PR 记录真实证据。
|
- [ ] 涉及数据表、索引、映射、查询或同步时,已验证执行计划、数据量边界和负载控制,并在 PR 记录真实证据。
|
||||||
- [ ] 已同步最新主分支及远端任务分支,未强推、未改写已发布历史。
|
- [ ] 已同步最新主分支及远端任务分支,未强推、未改写已发布历史。
|
||||||
- [ ] 已逐项审核最终差异;冲突处理保留双方有效行为,疑点已确认。
|
- [ ] 已逐项审核最终差异;冲突处理保留双方有效行为,疑点已确认。
|
||||||
|
- [ ] 已取得独立审核,或已在 PR 中记录仓库所有者/管理员针对最终提交的明确合并授权。
|
||||||
- [ ] 已完成受影响的验证,记录准确结果和提交 SHA。
|
- [ ] 已完成受影响的验证,记录准确结果和提交 SHA。
|
||||||
- [ ] PR 已说明范围、兼容性、验证、风险和回退方法。
|
- [ ] PR 已说明范围、兼容性、验证、风险和回退方法。
|
||||||
- [ ] 已明确报告当前状态:本地提交 / 已推送 / PR 待审核 / 已合并;没有把推送成功说成合并或部署成功。
|
- [ ] 已明确报告当前状态:本地提交 / 已推送 / PR 待审核 / 已合并;没有把推送成功说成合并或部署成功。
|
||||||
@ -198,6 +199,6 @@ npm --prefix frontend run check
|
|||||||
|
|
||||||
本文件规定协作流程,`AGENTS.md` 提供 AI 读取入口;它们不是权限控制,不能保证每个 AI 工具自动读取或遵守。使用不自动读取这些文件的工具时,开发者必须主动提供规则。
|
本文件规定协作流程,`AGENTS.md` 提供 AI 读取入口;它们不是权限控制,不能保证每个 AI 工具自动读取或遵守。使用不自动读取这些文件的工具时,开发者必须主动提供规则。
|
||||||
|
|
||||||
仓库管理员应在服务端落实以下保护,并以实际设置为准:保护 `main`、禁止直接及强制推送和删除、要求 PR 与独立审核、使过期审核失效、绑定真实运行的必需检查、限制合并权限、按个人发放最小权限凭据。没有真正运行的检查不能仅凭同名状态作为合并依据。
|
仓库管理员应在服务端落实以下保护,并以实际设置为准:保护 `main`、禁止直接及强制推送和删除、要求通过 PR 合并、使过期审核失效、绑定真实运行的必需检查、限制合并权限、按个人发放最小权限凭据。团队可默认要求独立审核;需要允许所有者/管理员授权例外时,应确保只有真实所有者或管理员能够执行并保留可审计记录。没有真正运行的检查不能仅凭同名状态作为合并依据。
|
||||||
|
|
||||||
**添加本文件不等于上述保护已经启用。** 此文档变更不修改仓库权限、分支保护、CI 或生产部署;如需启用服务端限制,应由管理员单独配置并验证。
|
**添加本文件不等于上述保护已经启用。** 此文档变更不修改仓库权限、分支保护、CI 或生产部署;如需启用服务端限制,应由管理员单独配置并验证。
|
||||||
|
|||||||
@ -59,8 +59,8 @@ git diff --stat origin/main...HEAD
|
|||||||
2. 本地已有修改先按 `仓库提交与合并规范.md` 提交到唯一任务分支,再同步最新 `origin/main`。
|
2. 本地已有修改先按 `仓库提交与合并规范.md` 提交到唯一任务分支,再同步最新 `origin/main`。
|
||||||
3. 如果主仓库已有更新,先合并到任务分支,逐处处理冲突,重新检查最终差异并运行受影响测试。
|
3. 如果主仓库已有更新,先合并到任务分支,逐处处理冲突,重新检查最终差异并运行受影响测试。
|
||||||
4. 禁止使用强制推送、`git reset --hard`、批量选择 `ours/theirs` 或复制旧目录覆盖新代码来解决冲突。
|
4. 禁止使用强制推送、`git reset --hard`、批量选择 `ours/theirs` 或复制旧目录覆盖新代码来解决冲突。
|
||||||
5. 将任务分支推送到主仓库,创建 PR,经过审核和验证后合并到 `main`。这里的“先推送主仓库”指推送任务分支并通过 PR 合并,禁止直接推送 `main`。
|
5. 将任务分支推送到主仓库并创建 PR;经过独立审核,或取得仓库所有者/管理员针对该 PR 最终提交的明确授权,并完成验证后合并到 `main`。这里的“先推送主仓库”指推送任务分支并通过 PR 合并,禁止直接推送 `main`。
|
||||||
6. PR 合并后再次执行 `git fetch origin`,记录准备部署的 `origin/main` 完整 SHA。部署 SHA 必须与审核通过的最终提交一致。
|
6. PR 合并后再次执行 `git fetch origin`,记录准备部署的 `origin/main` 完整 SHA。部署 SHA 必须与审核或所有者/管理员授权覆盖的最终提交一致。
|
||||||
|
|
||||||
如果本次变更涉及数据表、索引、查询、数据映射或同步任务,部署者还必须确认 PR 已按《仓库提交与合并规范》记录索引与执行计划证据、分批/并发/超时边界、大表锁影响和回退方式。证据缺失、依赖全表扫描、可能对系统库或第三方云库造成不可控负载时,不得部署。
|
如果本次变更涉及数据表、索引、查询、数据映射或同步任务,部署者还必须确认 PR 已按《仓库提交与合并规范》记录索引与执行计划证据、分批/并发/超时边界、大表锁影响和回退方式。证据缺失、依赖全表扫描、可能对系统库或第三方云库造成不可控负载时,不得部署。
|
||||||
|
|
||||||
@ -112,15 +112,14 @@ test "$(git rev-parse "origin/main^{commit}")" = "$DEPLOY_SHA" || {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
git switch main
|
git checkout main
|
||||||
git pull --ff-only origin main
|
git pull --ff-only origin main
|
||||||
test "$(git rev-parse HEAD)" = "$DEPLOY_SHA" || {
|
test "$(git rev-parse HEAD)" = "$DEPLOY_SHA" || {
|
||||||
echo '服务器 HEAD 与待部署 SHA 不一致,停止部署'
|
echo '服务器 HEAD 与待部署 SHA 不一致,停止部署'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
npm --prefix frontend ci
|
bash deploy/production/build-frontend.sh
|
||||||
npm --prefix frontend run build
|
|
||||||
if grep -q '^SOURCE_GIT_SHA=' /opt/cangku/shared/backend.env; then
|
if grep -q '^SOURCE_GIT_SHA=' /opt/cangku/shared/backend.env; then
|
||||||
sed -i "s/^SOURCE_GIT_SHA=.*/SOURCE_GIT_SHA=$DEPLOY_SHA/" /opt/cangku/shared/backend.env
|
sed -i "s/^SOURCE_GIT_SHA=.*/SOURCE_GIT_SHA=$DEPLOY_SHA/" /opt/cangku/shared/backend.env
|
||||||
else
|
else
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user