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); + } +}