routes_stock.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { Hono } from 'hono'
  2. import type { Variables, WithAuth } from "./middlewares.ts";
  3. import debug from "debug";
  4. import dayjs from 'dayjs'
  5. interface ApiResponse<T = any> {
  6. success: boolean;
  7. code?: string;
  8. message?: string;
  9. data?: T;
  10. }
  11. interface DateNote {
  12. date: string;
  13. memo: string;
  14. [key: string]: string;
  15. }
  16. const log = {
  17. api: debug("api:stock"),
  18. };
  19. export function createStockRoutes(withAuth: WithAuth) {
  20. const stockRoutes = new Hono<{ Variables: Variables }>()
  21. stockRoutes.get('/history', async (c) => {
  22. const apiClient = c.get('apiClient')
  23. const url = new URL(c.req.url)
  24. const code = url.searchParams.get("code") || '001339'
  25. const dh = 'dn' // 固定值
  26. if (!code || !dh) {
  27. return c.text("Missing required parameters", 400)
  28. }
  29. try {
  30. const license = Deno.env.get('STOCK_API_LICENSE')
  31. if (!license) {
  32. throw new Error('STOCK_API_LICENSE environment variable not set')
  33. }
  34. // 查询数据库中是否存在今天的数据
  35. const today = dayjs().format('YYYY-MM-DD')
  36. const existingData = await apiClient.database
  37. .table("stock_data")
  38. .select("*")
  39. .where("code", code)
  40. .where("updated_at", ">=", `${today} 00:00:00`)
  41. .first()
  42. if (existingData) {
  43. return c.json(existingData.data)
  44. }
  45. // 如果没有今天的数据,调用外部API
  46. const apiUrl = `http://api.mairui.club/hszbl/fsjy/${code}/${dh}/${license}`
  47. const response = await fetch(apiUrl, {
  48. method: "GET",
  49. headers: {
  50. "Accept": "application/json"
  51. }
  52. })
  53. if (!response.ok) {
  54. throw new Error(`API request failed with status ${response.status}`)
  55. }
  56. const newData = await response.json()
  57. // 更新或插入数据库
  58. await apiClient.database
  59. .table("stock_data")
  60. .insert({
  61. code,
  62. data: JSON.stringify(newData),
  63. created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  64. updated_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
  65. })
  66. .onConflict("code")
  67. .merge({
  68. data: JSON.stringify(newData),
  69. updated_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
  70. })
  71. .execute()
  72. return c.json(newData)
  73. } catch (error) {
  74. log.api("Error getting stock history:", error)
  75. return c.text("Internal Server Error", 500)
  76. }
  77. })
  78. stockRoutes.get('/memos', async (c) => {
  79. const apiClient = c.get('apiClient')
  80. const url = new URL(c.req.url)
  81. const code = url.searchParams.get("code")
  82. if (!code) {
  83. const response: ApiResponse<Array<DateNote>> = {
  84. success: true,
  85. data: []
  86. }
  87. return c.json(response)
  88. }
  89. try {
  90. const notes = await apiClient.database
  91. .table("date_notes")
  92. .select(["note_date as date", "note as memo"])
  93. .where("code", code)
  94. .orderBy("note_date", "asc")
  95. .execute() as DateNote[]
  96. if (!notes || notes.length === 0) {
  97. const response: ApiResponse<Array<DateNote>> = {
  98. success: true,
  99. data: []
  100. }
  101. return c.json(response)
  102. }
  103. notes.forEach((note) => {
  104. note.日期 = dayjs(note.date).format("YYYY-MM-DD")
  105. note.提示 = note.memo
  106. })
  107. const response: ApiResponse<Array<DateNote>> = {
  108. success: true,
  109. data: notes
  110. }
  111. return c.json(response)
  112. } catch (error) {
  113. log.api("Error fetching memo data:", error)
  114. const response: ApiResponse = {
  115. success: false,
  116. code: "SYSTEM.ERROR",
  117. message: error instanceof Error ? error.message : "获取备忘录数据失败"
  118. }
  119. return c.json(response, 500)
  120. }
  121. })
  122. return stockRoutes
  123. }