先短暂去除限制,上传迁移脚本
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m51s

This commit is contained in:
2026-07-09 18:23:29 +09:00
parent 346156ee9b
commit b38df737e1
4 changed files with 59 additions and 37 deletions

View File

@@ -279,17 +279,17 @@ func (h *ClientWalletHandler) CreateRecharge(c *fiber.Ctx) error {
return err
}
// 第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信
switch resolved.Asset.AssetType {
case "card":
if req.PaymentMethod != constants.RechargeMethodAlipay {
return errors.New(errors.CodeInvalidParam, "单卡资产只支持支付宝充值")
}
case "device":
if req.PaymentMethod != constants.RechargeMethodWechat {
return errors.New(errors.CodeInvalidParam, "设备资产只支持微信充值")
}
}
// 第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信(已暂时注释)
// switch resolved.Asset.AssetType {
// case "card":
// if req.PaymentMethod != constants.RechargeMethodAlipay {
// return errors.New(errors.CodeInvalidParam, "单卡资产只支持支付宝充值")
// }
// case "device":
// if req.PaymentMethod != constants.RechargeMethodWechat {
// return errors.New(errors.CodeInvalidParam, "设备资产只支持微信充值")
// }
// }
config, err := h.wechatConfigService.GetActiveConfig(resolved.SkipPermissionCtx)
if err != nil {

View File

@@ -229,17 +229,17 @@ func (s *Service) CreateOrder(ctx context.Context, customerID uint, req *dto.Cli
}()
if forceRecharge.NeedForceRecharge {
// 强充第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信
switch assetInfo.AssetType {
case "card":
if req.PaymentMethod != model.PaymentMethodAlipay {
return nil, errors.New(errors.CodeInvalidParam, "单卡资产强充只支持支付宝支付")
}
case constants.ResourceTypeDevice:
if req.PaymentMethod == model.PaymentMethodAlipay {
return nil, errors.New(errors.CodeInvalidParam, "设备资产强充只支持微信支付")
}
}
// 强充第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信(已暂时注释)
// switch assetInfo.AssetType {
// case "card":
// if req.PaymentMethod != model.PaymentMethodAlipay {
// return nil, errors.New(errors.CodeInvalidParam, "单卡资产强充只支持支付宝支付")
// }
// case constants.ResourceTypeDevice:
// if req.PaymentMethod == model.PaymentMethodAlipay {
// return nil, errors.New(errors.CodeInvalidParam, "设备资产强充只支持微信支付")
// }
// }
if req.PaymentMethod == model.PaymentMethodAlipay {
// 支付宝强充:不需要 app_type / OpenID
@@ -1253,19 +1253,19 @@ func (s *Service) PayOrder(ctx context.Context, customerID uint, orderID uint, r
return nil, errors.New(errors.CodeNeedRealname)
}
// 第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信
if req.PaymentMethod == model.PaymentMethodWechat || req.PaymentMethod == model.PaymentMethodAlipay {
switch order.OrderType {
case model.OrderTypeSingleCard:
if req.PaymentMethod != model.PaymentMethodAlipay {
return nil, errors.New(errors.CodeInvalidParam, "单卡资产只支持支付宝支付")
}
case model.OrderTypeDevice:
if req.PaymentMethod != model.PaymentMethodWechat {
return nil, errors.New(errors.CodeInvalidParam, "设备资产只支持微信支付")
}
}
}
// 第三方支付方式与资产类型必须匹配:单卡只允许支付宝,设备只允许微信(已暂时注释)
// if req.PaymentMethod == model.PaymentMethodWechat || req.PaymentMethod == model.PaymentMethodAlipay {
// switch order.OrderType {
// case model.OrderTypeSingleCard:
// if req.PaymentMethod != model.PaymentMethodAlipay {
// return nil, errors.New(errors.CodeInvalidParam, "单卡资产只支持支付宝支付")
// }
// case model.OrderTypeDevice:
// if req.PaymentMethod != model.PaymentMethodWechat {
// return nil, errors.New(errors.CodeInvalidParam, "设备资产只支持微信支付")
// }
// }
// }
switch req.PaymentMethod {
case model.PaymentMethodWallet:

View File

@@ -139,6 +139,7 @@ class AgentMapping:
legacy_agent_id: str
legacy_agent_name: str
target_shop_code: Optional[str] # legacy_agent 模式下允许 null,表示该代理资产进平台库存
no_downtime: bool = False # 迁移时免停机:有生效套餐的卡直接以 network_status=1 导入
@dataclass
@@ -166,6 +167,11 @@ class Mapping:
return None
return self.series.get(str(legacy_series_id).strip())
def is_no_downtime_agent(self, legacy_agent_id: str) -> bool:
"""判断代理是否配置了免停机迁移。"""
agent = self.agents.get(str(legacy_agent_id or "").strip())
return agent.no_downtime if agent else False
def lookup_agent_shop_code(self, legacy_agent_id: str) -> Optional[str]:
"""按 agent_id 查 target_shop_code,空字符串视为 None(进平台库存)。"""
if not legacy_agent_id:
@@ -260,6 +266,7 @@ def load_mapping(config_dir: Path) -> Mapping:
legacy_agent_id=str(item["legacy_agent_id"]).strip(),
legacy_agent_name=str(item.get("legacy_agent_name") or ""),
target_shop_code=_opt_shop_code(item.get("target_shop_code"), "agents[].target_shop_code"),
no_downtime=bool(item.get("no_downtime", False)),
)
m.agents[a.legacy_agent_id] = a
@@ -536,6 +543,7 @@ def _append_agents(lines: list[str], mapping: Mapping) -> None:
f" - legacy_agent_id: {_yaml_scalar(item.legacy_agent_id)}",
f" legacy_agent_name: {_yaml_scalar(item.legacy_agent_name)}",
f" target_shop_code: {_yaml_scalar(item.target_shop_code)}",
f" no_downtime: {_yaml_scalar(item.no_downtime)}",
])

View File

@@ -621,6 +621,20 @@ def _write_iot_cards(
card_status = 2 if shop_code else 1
shop_id_expr = _shop_id_sub(shop_code)
# 免停机迁移:代理配置 no_downtime=true 且卡在奇成有当前生效正式套餐
# finalize 保证最后执行,此时 step2 套餐已落库,轮询系统不会在此之前触碰这张卡
is_no_downtime = bool(meta.current_meal_id) and mapping.is_no_downtime_agent(meta.agent_id)
if is_no_downtime:
activation_status = 1
real_name_status = 1 # 卡在线说明已实名,写 1 防止 EvaluateAndAct 因 not_realname 停机
network_status = 1
stop_reason_expr = "NULL"
else:
activation_status = 0
real_name_status = 0
network_status = 0
stop_reason_expr = _sql_str(_initial_polling_stop_reason(c))
f.write(
"INSERT INTO tb_iot_card (\n"
" iccid, iccid_19, iccid_20,\n"
@@ -636,9 +650,9 @@ def _write_iot_cards(
f" {_sql_str(c.iccid_full)}, {_sql_str(c.iccid_19)}, {iccid_20_expr},\n"
f" {carrier.target_carrier_id}, {_sql_str(carrier.target_carrier_type)}, {_sql_str(carrier.target_carrier_name)},\n"
f" {_sql_str(card_category)}, {_sql_str(mapping.migration_batch_no)},\n"
f" {card_status}, 0, 0, 0,\n"
f" {card_status}, {activation_status}, {real_name_status}, {network_status},\n"
f" 1, 1, {is_standalone},\n"
f" 'after_order', {_sql_str(_initial_polling_stop_reason(c))},\n"
f" 'after_order', {stop_reason_expr},\n"
f" {_sql_str(meta.msisdn) if meta.msisdn else 'NULL'}, "
f"{_sql_str(card_virtual_no) if card_virtual_no else 'NULL'}, "
f"{shop_id_expr}, {series_id_expr},\n"