diff --git a/db.sql b/db.sql
index 32e2179..94e35a5 100644
--- a/db.sql
+++ b/db.sql
@@ -57,6 +57,7 @@ DROP TABLE IF EXISTS `t_system_other_config`;
DROP TABLE IF EXISTS `t_warehouse_address`;
DROP TABLE IF EXISTS `t_notice_read`;
DROP TABLE IF EXISTS `t_notice`;
+DROP TABLE IF EXISTS `t_dict_type`;
DROP TABLE IF EXISTS `t_dict`;
DROP TABLE IF EXISTS `t_country`;
DROP TABLE IF EXISTS `t_recharge_record`;
@@ -493,7 +494,7 @@ CREATE TABLE `t_sales_product` (
`ProductDescription` TEXT COMMENT '产品描述',
`PreStorageScript` TEXT COMMENT '入库前脚本',
`CustomerGuideMarkdown` MEDIUMTEXT COMMENT '对客说明 Markdown',
- `IsActive` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用',
+ `IsActive` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`Scheme` VARCHAR(32) NOT NULL COMMENT '排货方案:costLowest/timeFastest/overallBest',
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
@@ -513,7 +514,7 @@ CREATE TABLE `t_sales_product_price` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
`SalesProductId` INT NOT NULL COMMENT '销售产品ID',
`SerialNumber` INT NOT NULL COMMENT '序号',
- `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
+ `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`PriceName` VARCHAR(64) NOT NULL COMMENT '价格名称',
`CostItem` VARCHAR(64) NOT NULL COMMENT '费用项',
`CostUnit` VARCHAR(16) NOT NULL COMMENT '费用单位:kg/票/件',
@@ -583,7 +584,7 @@ CREATE TABLE `t_sales_product_publish_record` (
`SalesProductId` INT NOT NULL COMMENT '销售产品ID',
`Version` VARCHAR(32) NOT NULL COMMENT '版本号,如 20231227112339',
`OperatorType` VARCHAR(16) NOT NULL COMMENT '操作类型:price/rules',
- `Status` VARCHAR(16) NOT NULL COMMENT '状态:生效/回滚/无效',
+ `Status` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None未设置,1=Active生效,2=Inactive失效',
`PublishUser` VARCHAR(64) NOT NULL COMMENT '发布人',
`PublishTime` DATETIME NOT NULL COMMENT '发布时间',
`Description` VARCHAR(512) NULL COMMENT '版本备注',
@@ -625,7 +626,7 @@ CREATE TABLE `t_service_channel` (
`ForecastCode` VARCHAR(128) NULL COMMENT '预报单号',
`Description` TEXT COMMENT '描述',
`PreScript` TEXT COMMENT '入库前脚本',
- `IsActive` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用',
+ `IsActive` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
@@ -642,7 +643,7 @@ CREATE TABLE `t_service_channel_price` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
`ServiceChannelId` INT NOT NULL COMMENT '服务渠道ID',
`SerialNumber` INT NOT NULL COMMENT '序号',
- `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
+ `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`PriceName` VARCHAR(64) NOT NULL COMMENT '价格名称',
`CostItem` VARCHAR(64) NOT NULL COMMENT '费用项',
`CostUnit` VARCHAR(16) NOT NULL COMMENT '费用单位:kg/票/件',
@@ -694,7 +695,7 @@ CREATE TABLE `t_service_channel_publish_record` (
`ServiceChannelId` INT NOT NULL COMMENT '服务渠道ID',
`Version` VARCHAR(32) NOT NULL COMMENT '版本号',
`OperatorType` VARCHAR(16) NOT NULL COMMENT '操作类型:price/rules',
- `Status` VARCHAR(16) NOT NULL COMMENT '状态:生效/回滚/无效',
+ `Status` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None未设置,1=Active生效,2=Inactive失效',
`PublishUser` VARCHAR(64) NOT NULL COMMENT '发布人',
`PublishTime` DATETIME NOT NULL COMMENT '发布时间',
`Description` VARCHAR(512) NULL COMMENT '版本备注',
@@ -808,7 +809,7 @@ CREATE TABLE `t_weight_rounding_info` (
CREATE TABLE `t_auto_trigger` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
- `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用',
+ `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`RuleName` VARCHAR(128) NULL COMMENT '规则名称',
`Priority` INT NOT NULL DEFAULT 100 COMMENT '优先级,数值越小越先执行',
`Operation` VARCHAR(32) NULL COMMENT '操作:拦截/警告/问题件',
@@ -834,7 +835,7 @@ CREATE TABLE `t_issue_template` (
`Title` VARCHAR(128) NOT NULL COMMENT '问题标题',
`Content` TEXT NOT NULL COMMENT '问题内容',
`NotifyMethod` VARCHAR(64) NOT NULL COMMENT '通知方式,逗号分隔',
- `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
+ `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用',
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
@@ -946,7 +947,7 @@ CREATE TABLE `t_customer` (
`Balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
`TotalQuota` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '总额度',
`UsedQuota` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '已用额度',
- `Status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-正常,2-冻结',
+ `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'CustomerStatus:0=None未设置,1=Disabled禁用,2=Normal正常,3=Frozen冻结',
`Remark` VARCHAR(512) NULL COMMENT '备注信息',
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人(系统/管理员账号)',
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -954,7 +955,7 @@ CREATE TABLE `t_customer` (
`UpdateTime` DATETIME NULL COMMENT '更新时间',
`Salesman` VARCHAR(32) NULL COMMENT '所属销售员账号',
`CustomerService` VARCHAR(32) NULL COMMENT '所属顾问',
- `CustomerGroup` TINYINT NULL COMMENT '客户分组:0-普通客户,1-VIP1,2-VIP2,3-VIP3,4-VIP4,5-VIP5',
+ `CustomerGroup` TINYINT NULL COMMENT 'CustomerGroup:0=None未设置,1=Normal普通,2=Vip1…6=Vip5',
`OrganizationId` CHAR(36) NULL COMMENT '归属销售组织 FK organization.Id',
`LastLoginIp` VARCHAR(64) NULL COMMENT '最后在线IP',
`LastLoginTime` DATETIME NULL COMMENT '最后在线时间',
@@ -980,10 +981,10 @@ CREATE TABLE `t_recharge_record` (
`SecretLevel` TINYINT NOT NULL DEFAULT 3 COMMENT '数据密级 1-4',
`SerialNumber` VARCHAR(64) NOT NULL COMMENT '流水号',
`RechargeTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '充值提交时间',
- `PayType` TINYINT NOT NULL COMMENT '支付方式:1-微信,2-支付宝',
+ `PayType` TINYINT NOT NULL COMMENT 'PayType:0=None未设置,1=Wechat微信,2=Alipay支付宝',
`RechargeAmount` DECIMAL(18,2) NOT NULL COMMENT '充值金额,>=0.01',
`RechargeScreenshot` VARCHAR(255) NULL COMMENT '充值截图URL',
- `RechargeStatus` TINYINT NOT NULL DEFAULT 0 COMMENT '充值状态:0-待审核,1-审核通过,2-审核驳回',
+ `RechargeStatus` TINYINT NOT NULL DEFAULT 1 COMMENT 'RechargeStatus:0=None未设置,1=Pending待审核,2=Approved通过,3=Rejected驳回',
`Remark` VARCHAR(512) NULL COMMENT '备注(客户/审核人填写)',
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -1019,7 +1020,7 @@ CREATE TABLE `t_dict` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
`DictType` VARCHAR(64) NOT NULL COMMENT '字典类型',
`DictSort` INT NOT NULL DEFAULT 0 COMMENT '排序值',
- `DictCode` VARCHAR(64) NOT NULL COMMENT '字典编码',
+ `DictCode` VARCHAR(64) NOT NULL COMMENT '字典编码:字符串类存语义码(camelCase);数值类存整数字符串如1/2,与对应枚举值一致(0=None不入库)',
`DictName` VARCHAR(128) NOT NULL COMMENT '字典名称',
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
@@ -1032,6 +1033,21 @@ CREATE TABLE `t_dict` (
KEY `idx_dict_type_sort` (`DictType`, `DictSort`, `Id`)
) COMMENT='系统字典表';
+CREATE TABLE `t_dict_type` (
+ `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
+ `DictType` VARCHAR(64) NOT NULL COMMENT '字典类型编码',
+ `Remark` VARCHAR(512) NULL COMMENT '字典类型备注',
+ `TypeSort` INT NOT NULL DEFAULT 0 COMMENT '左侧菜单排序',
+ `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
+ `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
+ `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
+ `CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `UpdateUserId` INT NULL COMMENT '更新人',
+ `UpdateTime` DATETIME NULL COMMENT '更新时间',
+ PRIMARY KEY (`Id`),
+ UNIQUE KEY `uk_dict_type_code` (`DictType`)
+) COMMENT='字典类型元数据表';
+
CREATE TABLE `t_system_other_config` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
`ConfigCode` VARCHAR(64) NOT NULL COMMENT '配置编码,如 track_service',
diff --git a/sql/migrate_enum_int_comments.sql b/sql/migrate_enum_int_comments.sql
new file mode 100644
index 0000000..46c68c5
--- /dev/null
+++ b/sql/migrate_enum_int_comments.sql
@@ -0,0 +1,74 @@
+-- 已有库增量:字节枚举字段注释统一 + 发布记录 Status 改 TINYINT + 旧 0 起编码迁移
+-- 对应 C# 枚举:EnableStatus / CustomerStatus / CustomerGroup / RechargeStatus / PayType / EsSnapshotStatus
+-- 约定:0=None 未设置;业务值从 1 起。执行前请备份。
+
+-- ---------- 1. 发布记录 Status:VARCHAR(中文) -> TINYINT(EsSnapshotStatus) ----------
+ALTER TABLE `t_sales_product_publish_record`
+ ADD COLUMN `StatusInt` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None,1=Active生效,2=Inactive失效' AFTER `OperatorType`;
+
+UPDATE `t_sales_product_publish_record`
+SET `StatusInt` = CASE
+ WHEN TRIM(`Status`) IN ('生效', 'active', 'Active', '1') THEN 1
+ WHEN TRIM(`Status`) IN ('失效', '无效', 'inactive', 'Inactive', '2') THEN 2
+ ELSE 1
+END;
+
+ALTER TABLE `t_sales_product_publish_record`
+ DROP COLUMN `Status`,
+ CHANGE COLUMN `StatusInt` `Status` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None未设置,1=Active生效,2=Inactive失效';
+
+ALTER TABLE `t_service_channel_publish_record`
+ ADD COLUMN `StatusInt` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None,1=Active生效,2=Inactive失效' AFTER `OperatorType`;
+
+UPDATE `t_service_channel_publish_record`
+SET `StatusInt` = CASE
+ WHEN TRIM(`Status`) IN ('生效', 'active', 'Active', '1') THEN 1
+ WHEN TRIM(`Status`) IN ('失效', '无效', 'inactive', 'Inactive', '2') THEN 2
+ ELSE 1
+END;
+
+ALTER TABLE `t_service_channel_publish_record`
+ DROP COLUMN `Status`,
+ CHANGE COLUMN `StatusInt` `Status` TINYINT NOT NULL DEFAULT 1 COMMENT 'EsSnapshotStatus:0=None未设置,1=Active生效,2=Inactive失效';
+
+-- ---------- 2. 旧 0 起编码 -> 1 起(若已迁移可跳过对应 UPDATE) ----------
+UPDATE `t_customer` SET `Status` = `Status` + 1 WHERE `Status` BETWEEN 0 AND 2;
+UPDATE `t_customer` SET `CustomerGroup` = `CustomerGroup` + 1 WHERE `CustomerGroup` BETWEEN 0 AND 5;
+UPDATE `t_recharge_record` SET `RechargeStatus` = `RechargeStatus` + 1 WHERE `RechargeStatus` BETWEEN 0 AND 2;
+
+UPDATE `t_sales_product` SET `IsActive` = CASE `IsActive` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `IsActive` END WHERE `IsActive` IN (0, 1);
+UPDATE `t_service_channel` SET `IsActive` = CASE `IsActive` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `IsActive` END WHERE `IsActive` IN (0, 1);
+UPDATE `t_sales_product_price` SET `Status` = CASE `Status` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `Status` END WHERE `Status` IN (0, 1);
+UPDATE `t_service_channel_price` SET `Status` = CASE `Status` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `Status` END WHERE `Status` IN (0, 1);
+UPDATE `t_auto_trigger` SET `Status` = CASE `Status` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `Status` END WHERE `Status` IN (0, 1);
+UPDATE `t_issue_template` SET `Status` = CASE `Status` WHEN 0 THEN 1 WHEN 1 THEN 2 ELSE `Status` END WHERE `Status` IN (0, 1);
+
+-- ---------- 3. 字段注释(可重复执行 MODIFY) ----------
+ALTER TABLE `t_customer`
+ MODIFY COLUMN `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'CustomerStatus:0=None未设置,1=Disabled禁用,2=Normal正常,3=Frozen冻结',
+ MODIFY COLUMN `CustomerGroup` TINYINT NULL COMMENT 'CustomerGroup:0=None未设置,1=Normal普通,2=Vip1…6=Vip5';
+
+ALTER TABLE `t_recharge_record`
+ MODIFY COLUMN `PayType` TINYINT NOT NULL COMMENT 'PayType:0=None未设置,1=Wechat微信,2=Alipay支付宝',
+ MODIFY COLUMN `RechargeStatus` TINYINT NOT NULL DEFAULT 1 COMMENT 'RechargeStatus:0=None未设置,1=Pending待审核,2=Approved通过,3=Rejected驳回';
+
+ALTER TABLE `t_sales_product`
+ MODIFY COLUMN `IsActive` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_service_channel`
+ MODIFY COLUMN `IsActive` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_sales_product_price`
+ MODIFY COLUMN `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_service_channel_price`
+ MODIFY COLUMN `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_auto_trigger`
+ MODIFY COLUMN `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_issue_template`
+ MODIFY COLUMN `Status` TINYINT NOT NULL DEFAULT 2 COMMENT 'EnableStatus:0=None未设置,1=Disabled禁用,2=Enabled启用';
+
+ALTER TABLE `t_dict`
+ MODIFY COLUMN `DictCode` VARCHAR(64) NOT NULL COMMENT '字典编码:字符串类存语义码;数值类存整数字符串如1/2,与枚举值一致(0=None不入库)';
diff --git a/sql/migrate_t_dict_type.sql b/sql/migrate_t_dict_type.sql
new file mode 100644
index 0000000..f0d306b
--- /dev/null
+++ b/sql/migrate_t_dict_type.sql
@@ -0,0 +1,90 @@
+-- 已有库增量:字典类型元数据 t_dict_type(类型编码 + 备注 + 排序)
+-- 对应功能:系统 → 数据字典,左侧类型列表与类型备注
+-- 可重复执行(IF NOT EXISTS / INSERT IGNORE;预置名称直接覆盖 Remark、TypeSort)
+
+CREATE TABLE IF NOT EXISTS `t_dict_type` (
+ `Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
+ `DictType` VARCHAR(64) NOT NULL COMMENT '字典类型编码,与 t_dict.DictType 对应',
+ `Remark` VARCHAR(512) NULL COMMENT '字典类型备注',
+ `TypeSort` INT NOT NULL DEFAULT 0 COMMENT '左侧菜单排序,值越小越靠前',
+ `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
+ `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
+ `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
+ `CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `UpdateUserId` INT NULL COMMENT '更新人',
+ `UpdateTime` DATETIME NULL COMMENT '更新时间',
+ PRIMARY KEY (`Id`),
+ UNIQUE KEY `uk_dict_type_code` (`DictType`)
+) COMMENT='字典类型元数据表';
+
+-- 从已有字典项回填类型(无备注)
+INSERT IGNORE INTO `t_dict_type` (`DictType`, `Remark`, `TypeSort`, `CreateUserId`)
+SELECT DISTINCT `DictType`, NULL, 0, 0
+FROM `t_dict`
+WHERE `IsDeleted` = 0 AND TRIM(`DictType`) <> '';
+
+-- 预置 OMS 字典类型中文名称(与前端 dict-batch-import.ts OMS_DICT_TYPE_REMARKS 对齐)
+-- 已存在记录时直接覆盖 Remark、TypeSort
+INSERT INTO `t_dict_type` (`DictType`, `TypeSort`, `Remark`, `CreateUserId`)
+VALUES
+ ('area_type', 1, '分区类型', 0),
+ ('auto_trigger_operation', 2, '自动触发操作', 0),
+ ('auto_trigger_timing', 3, '自动触发时机', 0),
+ ('billable_weight', 4, '计重取值', 0),
+ ('calculation_formula', 5, '计算公式', 0),
+ ('cargo_attribute', 6, '货物属性', 0),
+ ('claim_type', 7, '理赔类型', 0),
+ ('cost_unit', 8, '计费单位', 0),
+ ('currency', 9, '币种', 0),
+ ('customer_account_flow_biz_type', 10, '账户流水业务类型', 0),
+ ('customer_address_type', 11, '客户地址类型', 0),
+ ('customer_group', 12, '客户组', 0),
+ ('customer_status', 13, '客户状态', 0),
+ ('customer_waybill', 14, '客户面单', 0),
+ ('data_scope', 15, '数据范围', 0),
+ ('enable_status', 16, '启用状态', 0),
+ ('es_snapshot_status', 17, '快照生效状态', 0),
+ ('export_customs_mode', 18, '出口报关方式', 0),
+ ('fee_item', 19, '费用项', 0),
+ ('forecast_type', 20, '预报类型', 0),
+ ('issue_notify_method', 21, '问题通知方式', 0),
+ ('issue_type', 22, '问题件类型', 0),
+ ('label_generate_status', 23, '面单生成状态', 0),
+ ('label_template_type', 24, '面单模板类型', 0),
+ ('notice_type', 25, '通知类型', 0),
+ ('number_type', 26, '打印号码类型', 0),
+ ('operator_type', 27, '发布操作类型', 0),
+ ('order_batch_job_item_status', 28, '批量任务明细状态', 0),
+ ('order_batch_job_status', 29, '批量任务状态', 0),
+ ('order_fee_charging_stage', 30, '费用计费阶段', 0),
+ ('order_fee_type', 31, '费用类型', 0),
+ ('order_handle_status', 32, '订单处理子状态', 0),
+ ('order_source', 33, '订单来源', 0),
+ ('order_status', 34, '订单状态', 0),
+ ('order_stock_bind_status', 35, '库存绑定状态', 0),
+ ('order_stock_bind_type', 36, '库存绑定类型', 0),
+ ('order_task_status', 37, '订单导入任务状态', 0),
+ ('order_template_mapping_status', 38, '模板映射状态', 0),
+ ('order_template_type', 39, '订单模板类型', 0),
+ ('order_weight_type', 40, '件重尺类型', 0),
+ ('org_scope', 41, '组织范围', 0),
+ ('pay_type', 42, '支付方式', 0),
+ ('pricing_factor', 43, '定价因素', 0),
+ ('principal_type', 44, '主体类型', 0),
+ ('publish_record_type', 45, '发布记录类型', 0),
+ ('receiver_type', 46, '接收方类型', 0),
+ ('recharge_status', 47, '充值审核状态', 0),
+ ('sales_product_group', 48, '卖价分组', 0),
+ ('sales_store_auth_status', 49, '店铺授权状态', 0),
+ ('sales_store_fulfill_status', 50, '平台店铺履约状态', 0),
+ ('scheme', 51, '排货方案', 0),
+ ('secret_level', 52, '密级', 0),
+ ('warehouse_inbound_type', 53, '入库类型', 0),
+ ('warehouse_parcel_lifecycle', 54, '包裹生命周期', 0),
+ ('warehouse_receipt_status', 55, '收货状态', 0),
+ ('warehouse_type', 56, '仓库类型', 0),
+ ('weight_rule_condition', 57, '计重条件', 0),
+ ('yes_no', 58, '通用是否', 0)
+ON DUPLICATE KEY UPDATE
+ `TypeSort` = VALUES(`TypeSort`),
+ `Remark` = VALUES(`Remark`);
diff --git a/src/Oms.Application/DataPerm/DataPermConstants.cs b/src/Oms.Application/DataPerm/DataPermConstants.cs
index b1b4b3d..9c07a43 100644
--- a/src/Oms.Application/DataPerm/DataPermConstants.cs
+++ b/src/Oms.Application/DataPerm/DataPermConstants.cs
@@ -66,11 +66,11 @@ public static class DataPermConstants
public static readonly IReadOnlyList<(int Value, string Label)> CustomerGroupOptions =
[
- (0, "默认"),
- (1, "VIP"),
- (2, "重点"),
- (3, "普通"),
- (4, "试用"),
- (5, "停用")
+ (1, "普通客户"),
+ (2, "VIP1"),
+ (3, "VIP2"),
+ (4, "VIP3"),
+ (5, "VIP4"),
+ (6, "VIP5")
];
}
diff --git a/src/Oms.Application/Dtos/System/DictTypeOutput.cs b/src/Oms.Application/Dtos/System/DictTypeOutput.cs
new file mode 100644
index 0000000..d327e2b
--- /dev/null
+++ b/src/Oms.Application/Dtos/System/DictTypeOutput.cs
@@ -0,0 +1,32 @@
+namespace Oms.Application.Dtos.System;
+
+///
+/// 字典类型列表项。
+///
+public class DictTypeOutput
+{
+ ///
+ /// 元数据主键;仅存在于 t_dict_type 时有值。
+ ///
+ public int? Id { get; set; }
+
+ ///
+ /// 字典类型编码。
+ ///
+ public string DictType { get; set; } = string.Empty;
+
+ ///
+ /// 字典类型备注。
+ ///
+ public string? Remark { get; set; }
+
+ ///
+ /// 左侧菜单排序。
+ ///
+ public int TypeSort { get; set; }
+
+ ///
+ /// 该类型下字典项数量。
+ ///
+ public int ItemCount { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/System/DictTypeSaveInput.cs b/src/Oms.Application/Dtos/System/DictTypeSaveInput.cs
new file mode 100644
index 0000000..35f44b7
--- /dev/null
+++ b/src/Oms.Application/Dtos/System/DictTypeSaveInput.cs
@@ -0,0 +1,32 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Oms.Application.Dtos.System;
+
+///
+/// 保存字典类型元数据入参(新增或更新备注)。
+///
+public class DictTypeSaveInput
+{
+ ///
+ /// 元数据主键;更新时传入。
+ ///
+ public int? Id { get; set; }
+
+ ///
+ /// 字典类型编码。
+ ///
+ [Required]
+ [StringLength(64)]
+ public string DictType { get; set; } = string.Empty;
+
+ ///
+ /// 字典类型备注。
+ ///
+ [StringLength(512)]
+ public string? Remark { get; set; }
+
+ ///
+ /// 左侧菜单排序。
+ ///
+ public int TypeSort { get; set; }
+}
diff --git a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
index 578c4e2..e34139e 100644
--- a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
+++ b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
@@ -4,6 +4,7 @@ using Oms.Application.Dtos.Shared;
using Oms.Application.Shared;
using Oms.Domain.ChannelsAggregate.Consts;
using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.Shared;
using Oms.Domain.Shared.Enums;
namespace Oms.Application.Services.Channels;
@@ -148,7 +149,7 @@ public partial class SalesProductService
SalesProductId = entity.SalesProductId,
Version = entity.Version,
OperatorType = entity.OperatorType,
- Status = entity.Status,
+ Status = EnumDisplayNames.Of(entity.Status),
PublishUser = entity.PublishUser,
PublishTime = entity.PublishTime,
Description = entity.Description,
@@ -235,7 +236,7 @@ public partial class SalesProductService
SalesProductId = salesProductId,
Version = DateTime.Now.ToString("yyyyMMddHHmmss"),
OperatorType = operatorType,
- Status = "生效",
+ Status = EsSnapshotStatus.Active,
PublishUser = string.IsNullOrWhiteSpace(CurrentUser.UserName) ? CurrentUser.GetId().ToString() : CurrentUser.UserName!,
PublishTime = DateTime.Now,
Description = description,
diff --git a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs
index d8d6500..77799e4 100644
--- a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs
+++ b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs
@@ -2,6 +2,7 @@ using Oms.Application.Dtos.Channels;
using Oms.Application.Dtos.Shared;
using Oms.Application.Shared;
using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.Shared;
using Oms.Domain.Shared.Enums;
namespace Oms.Application.Services.Channels;
@@ -162,7 +163,7 @@ public partial class ServiceChannelService
ServiceChannelId = entity.ServiceChannelId,
Version = entity.Version,
OperatorType = entity.OperatorType,
- Status = entity.Status,
+ Status = EnumDisplayNames.Of(entity.Status),
PublishUser = entity.PublishUser,
PublishTime = entity.PublishTime,
Description = entity.Description,
@@ -230,7 +231,7 @@ public partial class ServiceChannelService
ServiceChannelId = serviceChannelId,
Version = DateTime.Now.ToString("yyyyMMddHHmmss"),
OperatorType = operatorType,
- Status = "生效",
+ Status = EsSnapshotStatus.Active,
PublishUser = string.IsNullOrWhiteSpace(CurrentUser.UserName) ? CurrentUser.GetId().ToString() : CurrentUser.UserName!,
PublishTime = DateTime.Now,
Description = description,
diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobProcessor.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobProcessor.cs
index 543d472..2167735 100644
--- a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobProcessor.cs
+++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobProcessor.cs
@@ -110,7 +110,11 @@ public sealed class OrderBatchJobProcessor(
}
await RefreshJobSummaryAsync(item.JobId, cancellationToken);
+ return;
}
+
+ // 系统失败:抛出让 EventBus 重试,耗尽后进 DLX
+ throw new InvalidOperationException(result.FailReason ?? "批量任务系统失败");
}
private async Task RefreshJobSummaryAsync(int jobId, CancellationToken cancellationToken)
@@ -169,11 +173,11 @@ public sealed class OrderBatchJobProcessor(
if (job.Status is OrderBatchJobStatusCodes.Completed or OrderBatchJobStatusCodes.Failed)
{
- task.Status = "completed";
+ task.Status = OrderTaskStatusCodes.Completed;
}
else if (job.Status == OrderBatchJobStatusCodes.Running)
{
- task.Status = "processing";
+ task.Status = OrderTaskStatusCodes.Processing;
}
await orderTaskRepository.UpdateAsync(task, cancellationToken: cancellationToken);
diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
index cf1e869..1f4c09c 100644
--- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
@@ -384,7 +384,7 @@ public partial class OrderService
Quantity = parsedRows.Count,
SuccessCount = 0,
FailCount = 0,
- Status = "processing",
+ Status = OrderTaskStatusCodes.Processing,
TemplateId = template.Id,
CustomerId = effectiveCustomer.Id
});
@@ -456,7 +456,7 @@ public partial class OrderService
Quantity = 0,
SuccessCount = 0,
FailCount = 0,
- Status = "processing",
+ Status = OrderTaskStatusCodes.Processing,
TemplateId = template.Id,
CustomerId = effectiveCustomer.Id
});
@@ -500,7 +500,7 @@ public partial class OrderService
task.Quantity = quantity;
task.SuccessCount = successCount;
task.FailCount = failCount;
- task.Status = "completed";
+ task.Status = OrderTaskStatusCodes.Completed;
await OrderTaskRepository.UpdateAsync(task);
return new OrderUploadOutput { TaskId = task.Id };
}
diff --git a/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs b/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs
index c25c31d..1e1677a 100644
--- a/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs
+++ b/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs
@@ -8,6 +8,7 @@ using Oms.Domain.ChannelsAggregate.Entities;
using Oms.Domain.CustomersAggregate.Enums;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
+using Oms.Domain.Shared;
namespace Oms.Application.Services.Orders;
@@ -397,7 +398,7 @@ internal static class ParcelBillingCalculator
HasEnabledReceivablePriceItems(priceSnapshot);
public static bool HasEnabledReceivablePriceItems(SalesProductPricePublishSnapshotDto priceSnapshot) =>
- priceSnapshot.Prices.Any(x => x.Status == 1);
+ priceSnapshot.Prices.Any(x => EnableStatusRules.IsEnabled(x.Status));
///
/// 收集卖价快照中所有启用价格项的明细分区键(去重)。
@@ -405,7 +406,7 @@ internal static class ParcelBillingCalculator
public static IReadOnlyList CollectDistinctDetailZoneKeys(SalesProductPricePublishSnapshotDto priceSnapshot)
{
var zones = new HashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var price in priceSnapshot.Prices.Where(x => x.Status == 1))
+ foreach (var price in priceSnapshot.Prices.Where(x => EnableStatusRules.IsEnabled(x.Status)))
{
foreach (var detail in price.Details ?? [])
{
@@ -434,7 +435,7 @@ internal static class ParcelBillingCalculator
{
var cargoLabel = GetCargoAttributeLabel(cargoAttribute);
var warehouseLabel = FormatWarehouseLabel(warehouseId, warehouseName);
- var enabledPrices = priceSnapshot.Prices.Where(x => x.Status == 1).ToList();
+ var enabledPrices = priceSnapshot.Prices.Where(x => EnableStatusRules.IsEnabled(x.Status)).ToList();
if (enabledPrices.Count == 0)
{
return "卖价快照中无启用的价格项";
@@ -671,7 +672,7 @@ internal static class ParcelBillingCalculator
HasEnabledReceivablePriceItems(priceSnapshot);
public static bool HasEnabledPayablePriceItems(IReadOnlyList prices) =>
- prices.Any(x => x.Status == 1);
+ prices.Any(x => EnableStatusRules.IsEnabled(x.Status));
public static bool HasEnabledForecastPayablePriceItems(IReadOnlyList prices) =>
HasEnabledPayablePriceItems(prices);
@@ -828,7 +829,7 @@ internal static class ParcelBillingCalculator
string? warehouseName = null)
{
var fees = new List();
- foreach (var priceItem in priceItems.Where(x => x.Status == 1))
+ foreach (var priceItem in priceItems.Where(x => EnableStatusRules.IsEnabled(x.Status)))
{
var detail = MatchPriceDetail(
priceItem.Details,
@@ -893,7 +894,7 @@ internal static class ParcelBillingCalculator
string? warehouseName = null)
{
var fees = new List();
- foreach (var priceItem in priceItems.Where(x => x.Status == 1))
+ foreach (var priceItem in priceItems.Where(x => EnableStatusRules.IsEnabled(x.Status)))
{
var detail = MatchPriceDetail(
priceItem.Details,
diff --git a/src/Oms.Application/Services/System/DictService.cs b/src/Oms.Application/Services/System/DictService.cs
index d1bad20..2bd2737 100644
--- a/src/Oms.Application/Services/System/DictService.cs
+++ b/src/Oms.Application/Services/System/DictService.cs
@@ -17,6 +17,8 @@ public class DictService : ApplicationService
{
private IDictRepository DictRepository => LazyServiceProvider.GetRequiredService();
+ private IDictTypeRepository DictTypeRepository => LazyServiceProvider.GetRequiredService();
+
///
/// 查询字典列表。
///
@@ -28,12 +30,13 @@ public class DictService : ApplicationService
var predicate = BuildListPredicate(input);
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
- var items = await DictRepository.GetPagedListAsync(
+ // Fake GetPagedListAsync(sorting) 会丢失 Where:total 正确但 items 返回全量
+ var (items, total) = await RepositoryPageHelper.GetPageAsync(
+ DictRepository,
predicate,
pageIndex,
pageSize,
- new Dictionary { [nameof(Dict.DictSort)] = true, [nameof(Dict.Id)] = false });
- var total = await DictRepository.CountAsync(predicate);
+ q => q.OrderBy(x => x.DictSort).ThenByDescending(x => x.Id));
return new OmsPageResult
{
@@ -42,6 +45,142 @@ public class DictService : ApplicationService
};
}
+ ///
+ /// 查询字典类型列表(含备注与项数)。
+ ///
+ /// 可选:按类型编码或备注模糊搜索。
+ /// 字典类型列表。
+ [HttpGet("/system/dict/type/list")]
+ public async Task> GetTypeListAsync([FromQuery] string? keyword)
+ {
+ var kw = keyword?.Trim();
+ var metaRows = await DictTypeRepository.GetListAsync();
+ var countRows = await DictRepository.GetItemCountByTypeAsync();
+ var countMap = countRows
+ .Select(x => (Type: x.DictType.Trim(), x.ItemCount))
+ .Where(x => !string.IsNullOrWhiteSpace(x.Type))
+ .GroupBy(x => x.Type, StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(g => g.Key, g => g.Sum(x => x.ItemCount), StringComparer.OrdinalIgnoreCase);
+
+ var merged = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var meta in metaRows)
+ {
+ var type = meta.DictType.Trim();
+ if (string.IsNullOrWhiteSpace(type))
+ {
+ continue;
+ }
+
+ merged[type] = new DictTypeOutput
+ {
+ Id = meta.Id,
+ DictType = type,
+ Remark = meta.Remark,
+ TypeSort = meta.TypeSort,
+ ItemCount = countMap.TryGetValue(type, out var count) ? count : 0
+ };
+ }
+
+ foreach (var (dictType, itemCount) in countRows)
+ {
+ var type = dictType.Trim();
+ if (string.IsNullOrWhiteSpace(type) || merged.ContainsKey(type))
+ {
+ continue;
+ }
+
+ merged[type] = new DictTypeOutput
+ {
+ DictType = type,
+ ItemCount = itemCount
+ };
+ }
+
+ IEnumerable result = merged.Values;
+ if (!string.IsNullOrWhiteSpace(kw))
+ {
+ result = result.Where(x =>
+ x.DictType.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
+ (!string.IsNullOrWhiteSpace(x.Remark) && x.Remark.Contains(kw, StringComparison.OrdinalIgnoreCase)));
+ }
+
+ return result
+ .OrderBy(x => x.TypeSort)
+ .ThenBy(x => x.DictType, StringComparer.OrdinalIgnoreCase)
+ .ToList();
+ }
+
+ ///
+ /// 保存字典类型元数据(新增或更新备注)。
+ ///
+ /// 入参:字典类型元数据。
+ /// 无返回体。
+ [HttpPost("/system/dict/type/save")]
+ public async Task SaveTypeAsync(DictTypeSaveInput input)
+ {
+ var dictType = input.DictType?.Trim() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(dictType))
+ {
+ throw new BusinessException("字典类型不能为空");
+ }
+
+ var remark = string.IsNullOrWhiteSpace(input.Remark) ? null : input.Remark.Trim();
+ DictTypeMeta? entity = null;
+ if (input.Id.HasValue && input.Id.Value > 0)
+ {
+ entity = await DictTypeRepository.FirstOrDefaultAsync(x => x.Id == input.Id.Value);
+ }
+
+ entity ??= await DictTypeRepository.GetByDictTypeAsync(dictType);
+ if (entity == null)
+ {
+ var existedType = await DictTypeRepository.GetByDictTypeAsync(dictType);
+ if (existedType != null)
+ {
+ throw new BusinessException("字典类型已存在");
+ }
+
+ await DictTypeRepository.InsertAsync(new DictTypeMeta
+ {
+ DictType = dictType,
+ Remark = remark,
+ TypeSort = input.TypeSort
+ });
+ return;
+ }
+
+ if (!string.Equals(entity.DictType, dictType, StringComparison.OrdinalIgnoreCase))
+ {
+ var duplicate = await DictTypeRepository.GetByDictTypeAsync(dictType);
+ if (duplicate != null && duplicate.Id != entity.Id)
+ {
+ throw new BusinessException("字典类型已存在");
+ }
+ }
+
+ entity.DictType = dictType;
+ entity.Remark = remark;
+ entity.TypeSort = input.TypeSort;
+ await DictTypeRepository.UpdateAsync(entity);
+ }
+
+ ///
+ /// 删除字典类型元数据(不删除字典项)。
+ ///
+ /// 入参:目标主键。
+ /// 无返回体。
+ [HttpPost("/system/dict/type/delete")]
+ public async Task DeleteTypeAsync(IdInput input)
+ {
+ var entity = await DictTypeRepository.FirstOrDefaultAsync(x => x.Id == input.Id);
+ if (entity == null)
+ {
+ throw new BusinessException("字典类型不存在");
+ }
+
+ await DictTypeRepository.DeleteAsync(entity);
+ }
+
///
/// 新增字典。
///
@@ -50,13 +189,28 @@ public class DictService : ApplicationService
[HttpPost("/system/dict/create")]
public async Task CreateAsync(DictCreateInput input)
{
- await ValidateDictInputAsync(input.DictType, input.DictCode, input.DictName, null);
+ var dictType = input.DictType.Trim();
+ var dictCode = input.DictCode.Trim();
+ var dictName = input.DictName.Trim();
+ await ValidateDictInputAsync(dictType, dictCode, dictName, null);
+ await EnsureDictTypeMetaAsync(dictType);
+
+ var deleted = await DictRepository.GetByTypeAndCodeIncludingDeletedAsync(dictType, dictCode);
+ if (deleted != null && deleted.IsDeleted)
+ {
+ deleted.IsDeleted = false;
+ deleted.DictSort = input.DictSort;
+ deleted.DictName = dictName;
+ await DictRepository.UpdateAsync(deleted);
+ return;
+ }
+
await DictRepository.InsertAsync(new Dict
{
- DictType = input.DictType.Trim(),
+ DictType = dictType,
DictSort = input.DictSort,
- DictCode = input.DictCode.Trim(),
- DictName = input.DictName.Trim()
+ DictCode = dictCode,
+ DictName = dictName
});
}
@@ -70,6 +224,7 @@ public class DictService : ApplicationService
{
var entity = await GetDictOrThrowAsync(input.Id);
await ValidateDictInputAsync(input.DictType, input.DictCode, input.DictName, entity.Id);
+ await EnsureDictTypeMetaAsync(input.DictType.Trim());
entity.DictType = input.DictType.Trim();
entity.DictSort = input.DictSort;
entity.DictCode = input.DictCode.Trim();
@@ -148,4 +303,24 @@ public class DictService : ApplicationService
UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime)
};
}
+
+ private async Task EnsureDictTypeMetaAsync(string dictType)
+ {
+ if (string.IsNullOrWhiteSpace(dictType))
+ {
+ return;
+ }
+
+ var existed = await DictTypeRepository.GetByDictTypeAsync(dictType);
+ if (existed != null)
+ {
+ return;
+ }
+
+ await DictTypeRepository.InsertAsync(new DictTypeMeta
+ {
+ DictType = dictType,
+ TypeSort = 0
+ });
+ }
}
diff --git a/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPublishRecord.cs b/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPublishRecord.cs
index a30a3fb..a0c42a4 100644
--- a/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPublishRecord.cs
+++ b/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPublishRecord.cs
@@ -1,4 +1,5 @@
using Oms.Domain.Shared;
+using Oms.Domain.Shared.Enums;
using SqlSugar;
namespace Oms.Domain.ChannelsAggregate.Entities;
@@ -12,7 +13,7 @@ public class SalesProductPublishRecord : OmsAuditedEntity
public string OperatorType { get; set; } = string.Empty;
- public string Status { get; set; } = string.Empty;
+ public EsSnapshotStatus Status { get; set; } = EsSnapshotStatus.Active;
public string PublishUser { get; set; } = string.Empty;
diff --git a/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPublishRecord.cs b/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPublishRecord.cs
index eeaaa27..3706adc 100644
--- a/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPublishRecord.cs
+++ b/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPublishRecord.cs
@@ -1,4 +1,5 @@
using Oms.Domain.Shared;
+using Oms.Domain.Shared.Enums;
using SqlSugar;
namespace Oms.Domain.ChannelsAggregate.Entities;
@@ -12,7 +13,7 @@ public class ServiceChannelPublishRecord : OmsAuditedEntity
public string OperatorType { get; set; } = string.Empty;
- public string Status { get; set; } = string.Empty;
+ public EsSnapshotStatus Status { get; set; } = EsSnapshotStatus.Active;
public string PublishUser { get; set; } = string.Empty;
diff --git a/src/Oms.Domain/CustomersAggregate/Enums/CustomerGroup.cs b/src/Oms.Domain/CustomersAggregate/Enums/CustomerGroup.cs
index 198ab7e..a92de3a 100644
--- a/src/Oms.Domain/CustomersAggregate/Enums/CustomerGroup.cs
+++ b/src/Oms.Domain/CustomersAggregate/Enums/CustomerGroup.cs
@@ -1,11 +1,15 @@
namespace Oms.Domain.CustomersAggregate.Enums;
+///
+/// 客户组。0=None 未设置;业务值从 1 起。
+///
public enum CustomerGroup : byte
{
- Normal = 0,
- Vip1 = 1,
- Vip2 = 2,
- Vip3 = 3,
- Vip4 = 4,
- Vip5 = 5
+ None = 0,
+ Normal = 1,
+ Vip1 = 2,
+ Vip2 = 3,
+ Vip3 = 4,
+ Vip4 = 5,
+ Vip5 = 6
}
diff --git a/src/Oms.Domain/CustomersAggregate/Enums/CustomerStatus.cs b/src/Oms.Domain/CustomersAggregate/Enums/CustomerStatus.cs
index 465cc46..bb59aa2 100644
--- a/src/Oms.Domain/CustomersAggregate/Enums/CustomerStatus.cs
+++ b/src/Oms.Domain/CustomersAggregate/Enums/CustomerStatus.cs
@@ -1,8 +1,12 @@
namespace Oms.Domain.CustomersAggregate.Enums;
+///
+/// 客户状态。0=None 未设置;业务值从 1 起。
+///
public enum CustomerStatus : byte
{
- Disabled = 0,
- Normal = 1,
- Frozen = 2
+ None = 0,
+ Disabled = 1,
+ Normal = 2,
+ Frozen = 3
}
diff --git a/src/Oms.Domain/OrdersAggregate/Consts/OrderTaskStatusCodes.cs b/src/Oms.Domain/OrdersAggregate/Consts/OrderTaskStatusCodes.cs
new file mode 100644
index 0000000..54ffb85
--- /dev/null
+++ b/src/Oms.Domain/OrdersAggregate/Consts/OrderTaskStatusCodes.cs
@@ -0,0 +1,26 @@
+namespace Oms.Domain.OrdersAggregate.Consts;
+
+///
+/// 订单导入/批量任务状态(t_order_task.Status,字符串落库;与字典 order_task_status 对齐)。
+///
+public static class OrderTaskStatusCodes
+{
+ public const string Pending = "pending";
+ public const string Processing = "processing";
+ public const string Completed = "completed";
+
+ public static readonly HashSet All = new(StringComparer.OrdinalIgnoreCase)
+ {
+ Pending,
+ Processing,
+ Completed
+ };
+
+ public static string GetDisplayName(string? code) => code?.Trim().ToLowerInvariant() switch
+ {
+ Pending => "待处理",
+ Processing => "处理中",
+ Completed => "已完成",
+ _ => code ?? string.Empty
+ };
+}
diff --git a/src/Oms.Domain/RechargeAggregate/Enums/PayType.cs b/src/Oms.Domain/RechargeAggregate/Enums/PayType.cs
index 3aed3ab..3ef6adf 100644
--- a/src/Oms.Domain/RechargeAggregate/Enums/PayType.cs
+++ b/src/Oms.Domain/RechargeAggregate/Enums/PayType.cs
@@ -1,7 +1,11 @@
namespace Oms.Domain.RechargeAggregate.Enums;
+///
+/// 支付方式。0=None 未设置;业务值从 1 起。
+///
public enum PayType : byte
{
+ None = 0,
Wechat = 1,
Alipay = 2
}
diff --git a/src/Oms.Domain/RechargeAggregate/Enums/RechargeStatus.cs b/src/Oms.Domain/RechargeAggregate/Enums/RechargeStatus.cs
index 4fbf9cb..edd3918 100644
--- a/src/Oms.Domain/RechargeAggregate/Enums/RechargeStatus.cs
+++ b/src/Oms.Domain/RechargeAggregate/Enums/RechargeStatus.cs
@@ -1,8 +1,12 @@
namespace Oms.Domain.RechargeAggregate.Enums;
+///
+/// 充值审核状态。0=None 未设置;业务值从 1 起。
+///
public enum RechargeStatus : byte
{
- Pending = 0,
- Approved = 1,
- Rejected = 2
+ None = 0,
+ Pending = 1,
+ Approved = 2,
+ Rejected = 3
}
diff --git a/src/Oms.Domain/Shared/EnableStatusRules.cs b/src/Oms.Domain/Shared/EnableStatusRules.cs
new file mode 100644
index 0000000..2f11b1c
--- /dev/null
+++ b/src/Oms.Domain/Shared/EnableStatusRules.cs
@@ -0,0 +1,10 @@
+using Oms.Domain.Shared.Enums;
+
+namespace Oms.Domain.Shared;
+
+public static class EnableStatusRules
+{
+ public static bool IsEnabled(EnableStatus status) => status == EnableStatus.Enabled;
+
+ public static bool IsEnabled(byte status) => (EnableStatus)status == EnableStatus.Enabled;
+}
diff --git a/src/Oms.Domain/Shared/EnumDisplayNames.cs b/src/Oms.Domain/Shared/EnumDisplayNames.cs
new file mode 100644
index 0000000..d97053c
--- /dev/null
+++ b/src/Oms.Domain/Shared/EnumDisplayNames.cs
@@ -0,0 +1,59 @@
+using Oms.Domain.CustomersAggregate.Enums;
+using Oms.Domain.RechargeAggregate.Enums;
+using Oms.Domain.Shared.Enums;
+
+namespace Oms.Domain.Shared;
+
+///
+/// 字节枚举展示名(与 t_dict 中文名称对齐,供 API 出参等使用)。
+///
+public static class EnumDisplayNames
+{
+ public static string Of(EsSnapshotStatus status) => status switch
+ {
+ EsSnapshotStatus.Active => "生效",
+ EsSnapshotStatus.Inactive => "失效",
+ _ => string.Empty
+ };
+
+ public static string Of(EnableStatus status) => status switch
+ {
+ EnableStatus.Disabled => "禁用",
+ EnableStatus.Enabled => "启用",
+ _ => string.Empty
+ };
+
+ public static string Of(CustomerStatus status) => status switch
+ {
+ CustomerStatus.Disabled => "禁用",
+ CustomerStatus.Normal => "正常",
+ CustomerStatus.Frozen => "冻结",
+ _ => string.Empty
+ };
+
+ public static string Of(CustomerGroup? group) => group switch
+ {
+ CustomerGroup.Normal => "普通客户",
+ CustomerGroup.Vip1 => "VIP1",
+ CustomerGroup.Vip2 => "VIP2",
+ CustomerGroup.Vip3 => "VIP3",
+ CustomerGroup.Vip4 => "VIP4",
+ CustomerGroup.Vip5 => "VIP5",
+ _ => string.Empty
+ };
+
+ public static string Of(RechargeStatus status) => status switch
+ {
+ RechargeStatus.Pending => "待审核",
+ RechargeStatus.Approved => "审核通过",
+ RechargeStatus.Rejected => "审核驳回",
+ _ => string.Empty
+ };
+
+ public static string Of(PayType payType) => payType switch
+ {
+ PayType.Wechat => "微信",
+ PayType.Alipay => "支付宝",
+ _ => string.Empty
+ };
+}
diff --git a/src/Oms.Domain/Shared/Enums/EnableStatus.cs b/src/Oms.Domain/Shared/Enums/EnableStatus.cs
index 1e88adb..c48abbd 100644
--- a/src/Oms.Domain/Shared/Enums/EnableStatus.cs
+++ b/src/Oms.Domain/Shared/Enums/EnableStatus.cs
@@ -1,7 +1,11 @@
namespace Oms.Domain.Shared.Enums;
+///
+/// 启用状态。0=None 未设置;业务值从 1 起。
+///
public enum EnableStatus : byte
{
- Disabled = 0,
- Enabled = 1
+ None = 0,
+ Disabled = 1,
+ Enabled = 2
}
diff --git a/src/Oms.Domain/Shared/Enums/EsSnapshotStatus.cs b/src/Oms.Domain/Shared/Enums/EsSnapshotStatus.cs
new file mode 100644
index 0000000..03f0beb
--- /dev/null
+++ b/src/Oms.Domain/Shared/Enums/EsSnapshotStatus.cs
@@ -0,0 +1,12 @@
+namespace Oms.Domain.Shared.Enums;
+
+///
+/// ES/渠道发布快照生效状态(对应字典 es_snapshot_status 的数值落库,dictCode 为 active/inactive 仅作展示)。
+/// 0=None 未设置;业务值从 1 起。
+///
+public enum EsSnapshotStatus : byte
+{
+ None = 0,
+ Active = 1,
+ Inactive = 2
+}
diff --git a/src/Oms.Domain/SystemAggregate/Entities/DictTypeMeta.cs b/src/Oms.Domain/SystemAggregate/Entities/DictTypeMeta.cs
new file mode 100644
index 0000000..2fcf84f
--- /dev/null
+++ b/src/Oms.Domain/SystemAggregate/Entities/DictTypeMeta.cs
@@ -0,0 +1,27 @@
+using Oms.Domain.Shared;
+using SqlSugar;
+
+namespace Oms.Domain.SystemAggregate.Entities;
+
+///
+/// 字典类型元数据(类型编码 + 备注)。
+///
+[SugarTable("t_dict_type")]
+public class DictTypeMeta : OmsAuditedAggregateRoot
+{
+ ///
+ /// 字典类型编码,与 t_dict.DictType 对应。
+ ///
+ public string DictType { get; set; } = string.Empty;
+
+ ///
+ /// 字典类型备注说明。
+ ///
+ [SugarColumn(IsNullable = true, Length = 512)]
+ public string? Remark { get; set; }
+
+ ///
+ /// 左侧菜单排序,值越小越靠前。
+ ///
+ public int TypeSort { get; set; }
+}
diff --git a/src/Oms.Domain/SystemAggregate/Repositories/IDictRepository.cs b/src/Oms.Domain/SystemAggregate/Repositories/IDictRepository.cs
index 9384d44..d05a6aa 100644
--- a/src/Oms.Domain/SystemAggregate/Repositories/IDictRepository.cs
+++ b/src/Oms.Domain/SystemAggregate/Repositories/IDictRepository.cs
@@ -6,4 +6,8 @@ namespace Oms.Domain.SystemAggregate.Repositories;
public interface IDictRepository : IRepository
{
Task GetByTypeAndCodeAsync(string dictType, string dictCode, CancellationToken cancellationToken = default);
+
+ Task GetByTypeAndCodeIncludingDeletedAsync(string dictType, string dictCode, CancellationToken cancellationToken = default);
+
+ Task> GetItemCountByTypeAsync(CancellationToken cancellationToken = default);
}
diff --git a/src/Oms.Domain/SystemAggregate/Repositories/IDictTypeRepository.cs b/src/Oms.Domain/SystemAggregate/Repositories/IDictTypeRepository.cs
new file mode 100644
index 0000000..fb0e07f
--- /dev/null
+++ b/src/Oms.Domain/SystemAggregate/Repositories/IDictTypeRepository.cs
@@ -0,0 +1,9 @@
+using Fake.Domain.Repositories;
+using Oms.Domain.SystemAggregate.Entities;
+
+namespace Oms.Domain.SystemAggregate.Repositories;
+
+public interface IDictTypeRepository : IRepository
+{
+ Task GetByDictTypeAsync(string dictType, CancellationToken cancellationToken = default);
+}
diff --git a/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs b/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs
index 06ec59c..a5556cc 100644
--- a/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs
+++ b/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs
@@ -1,5 +1,6 @@
using Fake.DependencyInjection;
using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.Shared.Enums;
using Oms.Domain.ChannelsAggregate.Repositories;
using Oms.Domain.Shared.Enums;
using Oms.SqlSugarCore.Repositories;
@@ -227,7 +228,7 @@ public class SalesProductRepository : OmsSqlSugarRepository, ISale
var records = await db.Queryable()
.Where(x => x.SalesProductId == salesProductId
&& x.OperatorType == operatorType
- && x.Status == "生效"
+ && x.Status == EsSnapshotStatus.Active
&& x.PublishTime <= bindTime)
.OrderBy(x => x.PublishTime, SqlSugar.OrderByType.Desc)
.Take(1)
diff --git a/src/Oms.SqlSugarCore/Channels/ServiceChannelRepository.cs b/src/Oms.SqlSugarCore/Channels/ServiceChannelRepository.cs
index 9155af1..b18b318 100644
--- a/src/Oms.SqlSugarCore/Channels/ServiceChannelRepository.cs
+++ b/src/Oms.SqlSugarCore/Channels/ServiceChannelRepository.cs
@@ -1,5 +1,6 @@
using Fake.DependencyInjection;
using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.Shared.Enums;
using Oms.Domain.ChannelsAggregate.Repositories;
using Oms.Domain.Shared.Enums;
using Oms.SqlSugarCore.Repositories;
@@ -189,7 +190,7 @@ public class ServiceChannelRepository : OmsSqlSugarRepository, I
var records = await db.Queryable()
.Where(x => x.ServiceChannelId == serviceChannelId
&& x.OperatorType == operatorType
- && x.Status == "生效"
+ && x.Status == EsSnapshotStatus.Active
&& x.PublishTime <= bindTime)
.OrderBy(x => x.PublishTime, SqlSugar.OrderByType.Desc)
.Take(1)
diff --git a/src/Oms.SqlSugarCore/System/DictRepository.cs b/src/Oms.SqlSugarCore/System/DictRepository.cs
index fd69ef8..caba94a 100644
--- a/src/Oms.SqlSugarCore/System/DictRepository.cs
+++ b/src/Oms.SqlSugarCore/System/DictRepository.cs
@@ -2,6 +2,7 @@ using Fake.DependencyInjection;
using Oms.Domain.SystemAggregate.Entities;
using Oms.Domain.SystemAggregate.Repositories;
using Oms.SqlSugarCore.Repositories;
+using SqlSugar;
namespace Oms.SqlSugarCore.System;
@@ -11,4 +12,30 @@ public class DictRepository : OmsSqlSugarRepository, IDictRepository, ISco
{
return FirstOrDefaultAsync(x => x.DictType == dictType && x.DictCode == dictCode, cancellationToken);
}
+
+ public async Task GetByTypeAndCodeIncludingDeletedAsync(
+ string dictType,
+ string dictCode,
+ CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ return await db.Queryable()
+ .ClearFilter()
+ .FirstAsync(x => x.DictType == dictType && x.DictCode == dictCode, cancellationToken);
+ }
+
+ public async Task> GetItemCountByTypeAsync(CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ var rows = await db.Queryable()
+ .GroupBy(x => x.DictType)
+ .Select(x => new
+ {
+ DictType = x.DictType,
+ ItemCount = SqlFunc.AggregateCount(x.Id)
+ })
+ .ToListAsync(cancellationToken);
+
+ return rows.Select(x => (x.DictType, x.ItemCount)).ToList();
+ }
}
diff --git a/src/Oms.SqlSugarCore/System/DictTypeRepository.cs b/src/Oms.SqlSugarCore/System/DictTypeRepository.cs
new file mode 100644
index 0000000..b52693e
--- /dev/null
+++ b/src/Oms.SqlSugarCore/System/DictTypeRepository.cs
@@ -0,0 +1,14 @@
+using Fake.DependencyInjection;
+using Oms.Domain.SystemAggregate.Entities;
+using Oms.Domain.SystemAggregate.Repositories;
+using Oms.SqlSugarCore.Repositories;
+
+namespace Oms.SqlSugarCore.System;
+
+public class DictTypeRepository : OmsSqlSugarRepository, IDictTypeRepository, IScopedDependency
+{
+ public Task GetByDictTypeAsync(string dictType, CancellationToken cancellationToken = default)
+ {
+ return FirstOrDefaultAsync(x => x.DictType == dictType, cancellationToken);
+ }
+}