1use ek_core::{Field, SectionMap};
11
12#[derive(Debug, Clone)]
14pub struct AcctSpec {
15 pub broker_id: String,
16 pub user: String,
17 pub td_addr: String,
18 pub md_addr: String,
19 pub app_id: String,
20 pub auth_code: String,
21 pub classes: String,
23 pub md_user: String,
24 pub md_password: Option<String>,
26}
27
28pub fn derived_name(spec: &AcctSpec) -> Result<String, String> {
30 ek_ctp::derived_gateway_name(&spec.broker_id, &spec.user).ok_or_else(|| {
31 format!(
32 "派生注册名失败(broker={:?} 账号={:?}):字符不合名纪律",
33 spec.broker_id, spec.user
34 )
35 })
36}
37
38pub fn write_account(
46 store: &ek_cfg::Store,
47 spec: &AcctSpec,
48 password: &str,
49) -> Result<String, String> {
50 if spec.broker_id.is_empty()
51 || spec.user.is_empty()
52 || spec.td_addr.is_empty()
53 || spec.app_id.is_empty()
54 || spec.auth_code.is_empty()
55 {
56 return Err("缺必填要素(broker/user/td 前置/app_id/auth_code)".to_string());
57 }
58 if password.trim().is_empty() {
59 return Err("密码为空:档案不接受无密码账户(受理校验同款)".to_string());
60 }
61 let gw = derived_name(spec)?;
62 let ns = format!("ctp.{gw}");
63 ek_cfg::secret::set(
64 &ns,
65 "gw_td_password",
66 ek_cfg::SecretValue::from_bytes(password.trim().to_owned()),
67 )
68 .map_err(|e| format!("密码登记密库失败: {e:?}"))?;
69 let mut map = SectionMap::new(1)
70 .set("broker_id", Field::plain(spec.broker_id.trim()))
71 .set("td_addr", Field::plain(spec.td_addr.trim()))
72 .set("td_user", Field::plain(spec.user.trim()))
73 .set("td_app_id", Field::plain(spec.app_id.trim()))
74 .set("td_auth_code", Field::plain(spec.auth_code.trim()))
75 .set("td_password", Field::secret("gw_td_password"));
76 if !spec.md_addr.trim().is_empty() {
77 map = map.set("md_addr", Field::plain(spec.md_addr.trim()));
78 if !spec.md_user.trim().is_empty() {
79 map = map.set("md_user", Field::plain(spec.md_user.trim()));
80 }
81 if let Some(mp) = spec.md_password.as_deref() {
82 let mp = mp.trim();
83 if !mp.is_empty() {
84 ek_cfg::secret::set(&ns, "gw_md_password", ek_cfg::SecretValue::from_bytes(mp))
85 .map_err(|e| format!("行情密码登记密库失败: {e:?}"))?;
86 map = map.set("md_password", Field::secret("gw_md_password"));
87 }
88 }
89 }
90 let classes = spec.classes.trim();
91 if !classes.is_empty() {
92 for c in classes.split(',').map(str::trim) {
93 if c.chars().count() != 1 {
94 return Err(format!(
95 "product_classes 项 {c:?} 非单字符 CTP 码(D-PC-FILTER)"
96 ));
97 }
98 }
99 map = map.set("product_classes", Field::plain(classes));
100 }
101 store
102 .store(&ns, &map)
103 .map_err(|e| format!("档案落盘失败: {e:?}"))?;
104 Ok(gw)
105}