use crate::tcp::Tdx;
use crate::bytes_helper::{u16_from_le_bytes, u32_from_le_bytes};
#[derive(Debug, Clone)]
pub struct FinanceInfo<'d> {
pub send: Box<[u8]>,
pub market: u8,
pub code: &'d str,
pub response: Vec<u8>,
pub data: Vec<FinanceInfoData>,
}
impl<'d> FinanceInfo<'d> {
pub fn new(market: u8, code: &'d str) -> Self {
assert_eq!(code.len(), 6, "股票代码必须是6位");
let mut send = [0u8; Self::LEN];
send[0..14].copy_from_slice(Self::SEND);
send[14] = market;
send[15..21].copy_from_slice(code.as_bytes());
Self {
send: send.into(),
market,
code,
response: Vec::new(),
data: Vec::with_capacity(1),
}
}
}
impl<'a> Tdx for FinanceInfo<'a> {
type Item = [FinanceInfoData];
const SEND: &'static [u8] = &[
0x0c, 0x1f, 0x18, 0x76, 0x00, 0x01, 0x0b, 0x00, 0x0b, 0x00, 0x10, 0x00,
0x01, 0x00, ];
const TAG: &'static str = "财务信息";
const LEN: usize = 14 + 1 + 6;
fn send(&mut self) -> &[u8] {
&self.send
}
fn parse(&mut self, v: Vec<u8>) {
if v.len() < 137 {
eprintln!("⚠️ 财务信息数据长度不足: {} 字节(需要至少 137 字节)", v.len());
self.response = v;
self.data = Vec::new();
return;
}
let mut pos = 0;
pos += 2;
let market = v[pos];
pos += 1;
let code_bytes = &v[pos..pos + 6];
pos += 6;
let code = unsafe { std::str::from_utf8_unchecked(code_bytes) };
let code = String::from(code);
let liutongguben = f32_from_le_bytes(&v, pos); pos += 4;
let province = u16_from_le_bytes(&v, pos); pos += 2;
let industry = u16_from_le_bytes(&v, pos); pos += 2;
let updated_date = u32_from_le_bytes(&v, pos); pos += 4;
let ipo_date = u32_from_le_bytes(&v, pos); pos += 4;
let zongguben = f32_from_le_bytes(&v, pos); pos += 4;
let guojiagu = f32_from_le_bytes(&v, pos); pos += 4;
let faqirenfarengu = f32_from_le_bytes(&v, pos); pos += 4;
let farengu = f32_from_le_bytes(&v, pos); pos += 4;
let bgu = f32_from_le_bytes(&v, pos); pos += 4;
let hgu = f32_from_le_bytes(&v, pos); pos += 4;
let zhigonggu = f32_from_le_bytes(&v, pos); pos += 4;
let zongzichan = f32_from_le_bytes(&v, pos); pos += 4;
let liudongzichan = f32_from_le_bytes(&v, pos); pos += 4;
let gudingzichan = f32_from_le_bytes(&v, pos); pos += 4;
let wuxingzichan = f32_from_le_bytes(&v, pos); pos += 4;
let gudongrenshu = f32_from_le_bytes(&v, pos); pos += 4;
let liudongfuzhai = f32_from_le_bytes(&v, pos); pos += 4;
let changqifuzhai = f32_from_le_bytes(&v, pos); pos += 4;
let zibengongjijin = f32_from_le_bytes(&v, pos); pos += 4;
let jingzichan = f32_from_le_bytes(&v, pos); pos += 4;
let zhuyingshouru = f32_from_le_bytes(&v, pos); pos += 4;
let zhuyinglirun = f32_from_le_bytes(&v, pos); pos += 4;
let yingshouzhangkuan = f32_from_le_bytes(&v, pos); pos += 4;
let yingyelirun = f32_from_le_bytes(&v, pos); pos += 4;
let touzishouyu = f32_from_le_bytes(&v, pos); pos += 4;
let jingyingxianjinliu = f32_from_le_bytes(&v, pos); pos += 4;
let zongxianjinliu = f32_from_le_bytes(&v, pos); pos += 4;
let cunhuo = f32_from_le_bytes(&v, pos); pos += 4;
let lirunzonghe = f32_from_le_bytes(&v, pos); pos += 4;
let shuihoulirun = f32_from_le_bytes(&v, pos); pos += 4;
let jinglirun = f32_from_le_bytes(&v, pos); pos += 4;
let weifenpeilirun = f32_from_le_bytes(&v, pos); pos += 4;
let baoliu1 = f32_from_le_bytes(&v, pos); pos += 4;
let baoliu2 = f32_from_le_bytes(&v, pos);
let info = FinanceInfoData {
market,
code,
liutongguben: (liutongguben * 10000.0) as f64,
province,
industry,
updated_date,
ipo_date,
zongguben: (zongguben * 10000.0) as f64,
guojiagu: (guojiagu * 10000.0) as f64,
faqirenfarengu: (faqirenfarengu * 10000.0) as f64,
farengu: (farengu * 10000.0) as f64,
bgu: (bgu * 10000.0) as f64,
hgu: (hgu * 10000.0) as f64,
zhigonggu: (zhigonggu * 10000.0) as f64,
zongzichan: (zongzichan * 10000.0) as f64,
liudongzichan: (liudongzichan * 10000.0) as f64,
gudingzichan: (gudingzichan * 10000.0) as f64,
wuxingzichan: (wuxingzichan * 10000.0) as f64,
gudongrenshu: gudongrenshu as f64,
liudongfuzhai: (liudongfuzhai * 10000.0) as f64,
changqifuzhai: (changqifuzhai * 10000.0) as f64,
zibengongjijin: (zibengongjijin * 10000.0) as f64,
jingzichan: (jingzichan * 10000.0) as f64,
zhuyingshouru: (zhuyingshouru * 10000.0) as f64,
zhuyinglirun: (zhuyinglirun * 10000.0) as f64,
yingshouzhangkuan: (yingshouzhangkuan * 10000.0) as f64,
yingyelirun: (yingyelirun * 10000.0) as f64,
touzishouyu: (touzishouyu * 10000.0) as f64,
jingyingxianjinliu: (jingyingxianjinliu * 10000.0) as f64,
zongxianjinliu: (zongxianjinliu * 10000.0) as f64,
cunhuo: (cunhuo * 10000.0) as f64,
lirunzonghe: (lirunzonghe * 10000.0) as f64,
shuihoulirun: (shuihoulirun * 10000.0) as f64,
jinglirun: (jinglirun * 10000.0) as f64,
weifenpeilirun: (weifenpeilirun * 10000.0) as f64,
meigujingzichan: baoliu1 as f64,
baoliu2: baoliu2 as f64,
};
self.data.push(info);
self.response = v;
}
fn result(&self) -> &Self::Item {
&self.data
}
}
fn f32_from_le_bytes(data: &[u8], pos: usize) -> f32 {
let bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
f32::from_le_bytes(bytes)
}
#[derive(Debug, Default, Clone, serde::Serialize)]
pub struct FinanceInfoData {
pub market: u8,
pub code: String,
pub liutongguben: f64,
pub province: u16,
pub industry: u16,
pub updated_date: u32,
pub ipo_date: u32,
pub zongguben: f64,
pub guojiagu: f64,
pub faqirenfarengu: f64,
pub farengu: f64,
pub bgu: f64,
pub hgu: f64,
pub zhigonggu: f64,
pub zongzichan: f64,
pub liudongzichan: f64,
pub gudingzichan: f64,
pub wuxingzichan: f64,
pub gudongrenshu: f64,
pub liudongfuzhai: f64,
pub changqifuzhai: f64,
pub zibengongjijin: f64,
pub jingzichan: f64,
pub zhuyingshouru: f64,
pub zhuyinglirun: f64,
pub yingshouzhangkuan: f64,
pub yingyelirun: f64,
pub touzishouyu: f64,
pub jingyingxianjinliu: f64,
pub zongxianjinliu: f64,
pub cunhuo: f64,
pub lirunzonghe: f64,
pub shuihoulirun: f64,
pub jinglirun: f64,
pub weifenpeilirun: f64,
pub meigujingzichan: f64,
pub baoliu2: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_finance_info_new() {
let finance = FinanceInfo::new(0, "000001");
assert_eq!(finance.market, 0);
assert_eq!(finance.code, "000001");
assert_eq!(finance.send.len(), 21);
}
#[test]
fn test_finance_info_new_shanghai() {
let finance = FinanceInfo::new(1, "600000");
assert_eq!(finance.market, 1);
assert_eq!(finance.code, "600000");
}
#[test]
fn test_finance_info_send_bytes() {
let finance = FinanceInfo::new(0, "000001");
assert_eq!(&finance.send[0..14], &[0x0c, 0x1f, 0x18, 0x76, 0x00, 0x01, 0x0b, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x01, 0x00]);
assert_eq!(finance.send[14], 0);
assert_eq!(&finance.send[15..21], b"000001");
}
#[test]
#[should_panic(expected = "股票代码必须是6位")]
fn test_finance_info_invalid_code() {
FinanceInfo::new(0, "00001");
}
#[test]
fn test_connection() {
if std::env::var("RUSTDX_SKIP_INTEGRATION_TESTS").is_ok() {
println!("⚠️ 跳过集成测试 (RUSTDX_SKIP_INTEGRATION_TESTS 已设置)");
return;
}
println!("⚠️ 集成测试需要手动验证(需要实际TCP连接)");
}
}