tsystemlogmodel.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. package model
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/zeromicro/go-zero/core/stores/sqlx"
  9. )
  10. // TSystemLog is the audit log entry of one request/operation. It has no
  11. // json tags on purpose: the HMAC-SM3 chain signs the marshalled Go field
  12. // names, which must stay stable (see internal/audit/hmac.go).
  13. type TSystemLog struct {
  14. Id int64 `db:"id"` // 自增主键
  15. Iden string `db:"iden"` // 日志编号
  16. TraceId string `db:"trace_id"` // 全链路唯一Trace ID
  17. SpanId string `db:"span_id"` // 本次跨度唯一Span ID
  18. ParentSpanId sql.NullString `db:"parent_span_id"` // 父SpanID,用于构建调用树
  19. Type int64 `db:"type"` // 日志类型:1.系统日志;2.接口日志
  20. ServiceName string `db:"service_name"` // 所属服务或模块名
  21. UriPath sql.NullString `db:"uri_path"` // 接口请求路径
  22. HttpMethod sql.NullString `db:"http_method"` // 请求方法:GET/POST/PUT/DELETE
  23. Method string `db:"method"` // 请求业务方法
  24. Level string `db:"level"` // 日志级别:DEBUG/INFO/WARN/ERROR
  25. AppKey sql.NullString `db:"app_key"` // 调用接口应用标识
  26. UserId sql.NullString `db:"user_id"` // 系统用户ID
  27. UserName sql.NullString `db:"user_name"` // 系统用户名
  28. ClientIp sql.NullString `db:"client_ip"` // 客户端IP
  29. RequestHeader sql.NullString `db:"request_header"` // 请求头,脱敏后
  30. RequestBodyDigest string `db:"request_body_digest"` // 请求体摘要(SM3)
  31. RequestBodyFileKey string `db:"request_body_file_key"` // 请求体FileKey
  32. ResponseCode int64 `db:"response_code"` // 响应业务码
  33. ResponseBodyDigest string `db:"response_body_digest"` // 响应体摘要(SM3)
  34. ResponseBodyFileKey string `db:"response_body_file_key"` // 响应体FileKey
  35. ErrorMessage sql.NullString `db:"error_message"` // 错误提示,仅 ERROR 时填
  36. DurationMs int64 `db:"duration_ms"` // 本 Span 耗时(毫秒)
  37. CreatedAt time.Time `db:"created_at"` // 写入时间
  38. HMac string `db:"h_mac"` // Hmac值
  39. AuditStatus int64 `db:"audit_status"` // 审计状态:1.未审计;2.审计通过;3.审计异常
  40. }
  41. // tSystemLogRows is the full column list.
  42. var tSystemLogRows = strings.Join([]string{
  43. "`id`", "`iden`", "`trace_id`", "`span_id`", "`parent_span_id`", "`type`",
  44. "`service_name`", "`uri_path`", "`http_method`", "`method`", "`level`",
  45. "`app_key`", "`user_id`", "`user_name`", "`client_ip`", "`request_header`",
  46. "`request_body_digest`", "`request_body_file_key`", "`response_code`",
  47. "`response_body_digest`", "`response_body_file_key`", "`error_message`",
  48. "`duration_ms`", "`created_at`", "`h_mac`", "`audit_status`",
  49. }, ",")
  50. // SystemLogFilter mirrors SystemLogListReq; empty fields mean no filtering.
  51. // TypeValid marks that Type was provided (so type=0 can be an explicit
  52. // invalid request instead of "unfiltered").
  53. type SystemLogFilter struct {
  54. Iden string
  55. UserName string
  56. AppKey string
  57. UriPath string
  58. HttpMethod string
  59. CreatedStartTime string
  60. CreatedEndTime string
  61. ClientIp string
  62. ErrorMsg string
  63. AuditStatus int64
  64. Type int64
  65. TypeValid bool
  66. }
  67. type TSystemLogModel interface {
  68. // Insert stores a new audit entry; id and created_at are db generated
  69. // (created_at default CURRENT_TIMESTAMP, same as sdf-serve).
  70. Insert(ctx context.Context, data *TSystemLog) (sql.Result, error)
  71. // FindOneByIden returns the entry with the given iden.
  72. FindOneByIden(ctx context.Context, iden string) (*TSystemLog, error)
  73. // FindPage lists entries with optional filters and returns the page and
  74. // the total count, newest first (created_at DESC).
  75. FindPage(ctx context.Context, page, pageSize int64, f SystemLogFilter) ([]TSystemLog, int64, error)
  76. // FindByTraceId returns every entry of one trace, system-log first.
  77. FindByTraceId(ctx context.Context, traceId string) ([]TSystemLog, error)
  78. // FindByIdens returns the entries whose iden is contained in idens.
  79. FindByIdens(ctx context.Context, idens []string) ([]TSystemLog, error)
  80. // FindForArchive returns entries selected by the id list and/or the
  81. // created_at range (closed interval), ordered by created_at.
  82. FindForArchive(ctx context.Context, ids []int64, start, end string) ([]TSystemLog, error)
  83. // FindAllWithLimit returns up to threshold oldest entries for the
  84. // automatic archive job.
  85. FindAllWithLimit(ctx context.Context, threshold int) ([]*TSystemLog, error)
  86. // DeleteByIds batch-deletes rows by id; meant to run inside a tx.
  87. DeleteByIds(ctx context.Context, session sqlx.Session, ids []int64) error
  88. }
  89. type customTSystemLogModel struct {
  90. conn sqlx.SqlConn
  91. table string
  92. }
  93. var _ TSystemLogModel = (*customTSystemLogModel)(nil)
  94. // NewTSystemLogModel returns the model for the t_system_log table.
  95. func NewTSystemLogModel(conn sqlx.SqlConn) TSystemLogModel {
  96. return &customTSystemLogModel{
  97. conn: conn,
  98. table: "`t_system_log`",
  99. }
  100. }
  101. // tSystemLogInsertColumns is the insert column list; id and created_at are
  102. // generated by the database.
  103. func tSystemLogInsertColumns() string {
  104. return strings.Join([]string{
  105. "`iden`", "`trace_id`", "`span_id`", "`parent_span_id`", "`type`",
  106. "`service_name`", "`uri_path`", "`http_method`", "`method`", "`level`",
  107. "`app_key`", "`user_id`", "`user_name`", "`client_ip`", "`request_header`",
  108. "`request_body_digest`", "`request_body_file_key`", "`response_code`",
  109. "`response_body_digest`", "`response_body_file_key`", "`error_message`",
  110. "`duration_ms`", "`h_mac`", "`audit_status`",
  111. }, ",")
  112. }
  113. func (m *customTSystemLogModel) Insert(ctx context.Context, data *TSystemLog) (sql.Result, error) {
  114. query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  115. m.table, tSystemLogInsertColumns())
  116. ret, err := m.conn.ExecCtx(ctx, query,
  117. data.Iden, data.TraceId, data.SpanId, data.ParentSpanId, data.Type,
  118. data.ServiceName, data.UriPath, data.HttpMethod, data.Method, data.Level,
  119. data.AppKey, data.UserId, data.UserName, data.ClientIp, data.RequestHeader,
  120. data.RequestBodyDigest, data.RequestBodyFileKey, data.ResponseCode,
  121. data.ResponseBodyDigest, data.ResponseBodyFileKey, data.ErrorMessage,
  122. data.DurationMs, data.HMac, data.AuditStatus)
  123. return ret, err
  124. }
  125. func (m *customTSystemLogModel) FindOneByIden(ctx context.Context, iden string) (*TSystemLog, error) {
  126. var resp TSystemLog
  127. query := fmt.Sprintf("select %s from %s where `iden` = ? limit 1", tSystemLogRows, m.table)
  128. err := m.conn.QueryRowCtx(ctx, &resp, query, iden)
  129. switch err {
  130. case nil:
  131. return &resp, nil
  132. case sqlx.ErrNotFound:
  133. return nil, ErrNotFound
  134. default:
  135. return nil, err
  136. }
  137. }
  138. func (m *customTSystemLogModel) FindPage(ctx context.Context, page, pageSize int64, f SystemLogFilter) ([]TSystemLog, int64, error) {
  139. var conds []string
  140. var args []any
  141. if f.Iden != "" {
  142. conds = append(conds, "`iden` = ?")
  143. args = append(args, f.Iden)
  144. }
  145. if f.UserName != "" {
  146. conds = append(conds, "`user_name` like ? escape '\\\\'")
  147. args = append(args, "%"+escapeLike(f.UserName)+"%")
  148. }
  149. if f.AppKey != "" {
  150. conds = append(conds, "`app_key` = ?")
  151. args = append(args, f.AppKey)
  152. }
  153. if f.CreatedStartTime != "" {
  154. conds = append(conds, "`created_at` >= ?")
  155. args = append(args, f.CreatedStartTime)
  156. }
  157. if f.CreatedEndTime != "" {
  158. conds = append(conds, "`created_at` <= ?")
  159. args = append(args, f.CreatedEndTime)
  160. }
  161. if f.UriPath != "" {
  162. conds = append(conds, "`uri_path` = ?")
  163. args = append(args, f.UriPath)
  164. }
  165. if f.HttpMethod != "" {
  166. conds = append(conds, "`http_method` = ?")
  167. args = append(args, f.HttpMethod)
  168. }
  169. if f.ClientIp != "" {
  170. conds = append(conds, "`client_ip` = ?")
  171. args = append(args, f.ClientIp)
  172. }
  173. if f.ErrorMsg != "" {
  174. conds = append(conds, "`error_message` like ? escape '\\\\'")
  175. args = append(args, "%"+escapeLike(f.ErrorMsg)+"%")
  176. }
  177. if f.AuditStatus != 0 {
  178. conds = append(conds, "`audit_status` = ?")
  179. args = append(args, f.AuditStatus)
  180. }
  181. if f.TypeValid {
  182. conds = append(conds, "`type` = ?")
  183. args = append(args, f.Type)
  184. }
  185. where := ""
  186. if len(conds) > 0 {
  187. where = " where " + strings.Join(conds, " and ")
  188. }
  189. var total int64
  190. if err := m.conn.QueryRowCtx(ctx, &total, fmt.Sprintf("select count(*) from %s%s", m.table, where), args...); err != nil {
  191. return nil, 0, err
  192. }
  193. if total == 0 {
  194. return nil, 0, nil
  195. }
  196. offset := (page - 1) * pageSize
  197. args = append(args, pageSize, offset)
  198. query := fmt.Sprintf("select %s from %s%s order by `created_at` desc limit ? offset ?", tSystemLogRows, m.table, where)
  199. var resp []TSystemLog
  200. if err := m.conn.QueryRowsCtx(ctx, &resp, query, args...); err != nil {
  201. return nil, 0, err
  202. }
  203. return resp, total, nil
  204. }
  205. func (m *customTSystemLogModel) FindByTraceId(ctx context.Context, traceId string) ([]TSystemLog, error) {
  206. var resp []TSystemLog
  207. query := fmt.Sprintf(
  208. "select %s from %s where `trace_id` = ? order by case `type` when 1 then 0 when 2 then 1 else 2 end, `id` asc",
  209. tSystemLogRows, m.table)
  210. if err := m.conn.QueryRowsCtx(ctx, &resp, query, traceId); err != nil {
  211. return nil, err
  212. }
  213. return resp, nil
  214. }
  215. func (m *customTSystemLogModel) FindByIdens(ctx context.Context, idens []string) ([]TSystemLog, error) {
  216. placeholders := strings.TrimSuffix(strings.Repeat("?,", len(idens)), ",")
  217. args := make([]any, len(idens))
  218. for i, v := range idens {
  219. args[i] = v
  220. }
  221. var resp []TSystemLog
  222. query := fmt.Sprintf("select %s from %s where `iden` in (%s)", tSystemLogRows, m.table, placeholders)
  223. if err := m.conn.QueryRowsCtx(ctx, &resp, query, args...); err != nil {
  224. return nil, err
  225. }
  226. return resp, nil
  227. }
  228. func (m *customTSystemLogModel) FindForArchive(ctx context.Context, ids []int64, start, end string) ([]TSystemLog, error) {
  229. var conds []string
  230. var args []any
  231. if len(ids) > 0 {
  232. placeholders := strings.TrimSuffix(strings.Repeat("?,", len(ids)), ",")
  233. conds = append(conds, "`id` in ("+placeholders+")")
  234. for _, v := range ids {
  235. args = append(args, v)
  236. }
  237. }
  238. if start != "" && end != "" {
  239. conds = append(conds, "`created_at` >= ? and `created_at` <= ?")
  240. args = append(args, start, end)
  241. }
  242. where := ""
  243. if len(conds) > 0 {
  244. where = " where " + strings.Join(conds, " and ")
  245. }
  246. var resp []TSystemLog
  247. query := fmt.Sprintf("select %s from %s%s order by `created_at` asc", tSystemLogRows, m.table, where)
  248. if err := m.conn.QueryRowsCtx(ctx, &resp, query, args...); err != nil {
  249. return nil, err
  250. }
  251. return resp, nil
  252. }
  253. func (m *customTSystemLogModel) FindAllWithLimit(ctx context.Context, threshold int) ([]*TSystemLog, error) {
  254. var resp []*TSystemLog
  255. query := fmt.Sprintf("select %s from %s order by `created_at` asc limit ?", tSystemLogRows, m.table)
  256. if err := m.conn.QueryRowsCtx(ctx, &resp, query, threshold); err != nil {
  257. return nil, err
  258. }
  259. return resp, nil
  260. }
  261. func (m *customTSystemLogModel) DeleteByIds(ctx context.Context, session sqlx.Session, ids []int64) error {
  262. const deleteBatchSize = 1000
  263. for i := 0; i < len(ids); i += deleteBatchSize {
  264. end := i + deleteBatchSize
  265. if end > len(ids) {
  266. end = len(ids)
  267. }
  268. batchIds := ids[i:end]
  269. placeholders := strings.TrimSuffix(strings.Repeat("?,", len(batchIds)), ",")
  270. query := fmt.Sprintf("delete from t_system_log where id in (%s)", placeholders)
  271. args := make([]interface{}, len(batchIds))
  272. for j, v := range batchIds {
  273. args[j] = v
  274. }
  275. if _, err := session.ExecCtx(ctx, query, args...); err != nil {
  276. return err
  277. }
  278. }
  279. return nil
  280. }