This commit is contained in:
parent
747a0cb171
commit
5007fc9e24
|
|
@ -15,6 +15,7 @@ type BizApi interface {
|
||||||
NewsDraftDelete(c *gin.Context) //草稿删除
|
NewsDraftDelete(c *gin.Context) //草稿删除
|
||||||
NewsTag(c *gin.Context) //新闻打标签
|
NewsTag(c *gin.Context) //新闻打标签
|
||||||
NewsAsync(c *gin.Context) //异步同步数据
|
NewsAsync(c *gin.Context) //异步同步数据
|
||||||
|
NewsAsyncBatch(c *gin.Context) //一次性整批同步数据
|
||||||
NewsPullNew(c *gin.Context) //拉取新的数据
|
NewsPullNew(c *gin.Context) //拉取新的数据
|
||||||
QaList(c *gin.Context) //Q&A列表
|
QaList(c *gin.Context) //Q&A列表
|
||||||
QaAdd(c *gin.Context) //Q&A新增
|
QaAdd(c *gin.Context) //Q&A新增
|
||||||
|
|
|
||||||
|
|
@ -555,6 +555,28 @@ func (m *Controller) NewsAsync(c *gin.Context) {
|
||||||
m.OK(c, resp, len(resp.List), total)
|
m.OK(c, resp, len(resp.List), total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Controller) NewsAsyncBatch(c *gin.Context) {
|
||||||
|
var req proto.NewsAsyncBatchReq
|
||||||
|
|
||||||
|
if err := m.bindJSON(c, &req); err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Infof("....................NewsAsyncBatch request: %+v", req)
|
||||||
|
ctx := sessions.GetContext(c)
|
||||||
|
var ok bool
|
||||||
|
if ctx != nil {
|
||||||
|
ok = m.CheckPrivilege(c, ctx, privilege.NewsAccess)
|
||||||
|
}
|
||||||
|
ok = ok
|
||||||
|
resp, total, code := m.BizCore.NewsAsyncBatch(ctx, &req, true)
|
||||||
|
if !code.Ok() {
|
||||||
|
m.Error(c, code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.OK(c, resp, len(resp.List), total)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Controller) NewsPullNew(c *gin.Context) {
|
func (m *Controller) NewsPullNew(c *gin.Context) {
|
||||||
var req proto.NewsPullNewReq
|
var req proto.NewsPullNewReq
|
||||||
if err := m.bindJSON(c, &req); err != nil {
|
if err := m.bindJSON(c, &req); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -911,6 +911,42 @@ func (m *BizCore) NewsAsync(ctx *itypes.Context, req *proto.NewsAsyncReq, needEx
|
||||||
}, total, itypes.BizOK
|
}, total, itypes.BizOK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewsAsyncBatch:批量差异同步
|
||||||
|
func (m *BizCore) NewsAsyncBatch(ctx *itypes.Context, req *proto.NewsAsyncBatchReq, needExtra bool) (resp *proto.NewsAsyncResp, total int64, code itypes.BizCode) {
|
||||||
|
const batchSize = 5000 // 防止 SQL 占位符过长,按需调整
|
||||||
|
|
||||||
|
if len(req.List) == 0 {
|
||||||
|
return &proto.NewsAsyncResp{List: []*models.NewsDO{}}, 0, itypes.BizOK
|
||||||
|
}
|
||||||
|
|
||||||
|
var all []*models.NewsDO
|
||||||
|
|
||||||
|
for start := 0; start < len(req.List); start += batchSize {
|
||||||
|
end := start + batchSize
|
||||||
|
if end > len(req.List) {
|
||||||
|
end = len(req.List)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ① 把这一块转成 DAO 参数 ──
|
||||||
|
pairs := make([]dao.OrgDigestPair, end-start)
|
||||||
|
for i, v := range req.List[start:end] {
|
||||||
|
pairs[i] = dao.OrgDigestPair{OrgId: v.Org_Id, Digest: v.Digest}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ② 一次 SQL 抓差异 ──
|
||||||
|
chunk, _, err := m.newsDAO.QueryAsyncBatch(pairs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, itypes.NewBizCodeDatabaseError(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
all = append(all, chunk...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.NewsAsyncResp{
|
||||||
|
List: all,
|
||||||
|
}, int64(len(all)), itypes.BizOK
|
||||||
|
}
|
||||||
|
|
||||||
func (m *BizCore) NewsPullNew(ctx *itypes.Context, req *proto.NewsPullNewReq) (resp *proto.NewsPullNewResp, total int64, code itypes.BizCode) {
|
func (m *BizCore) NewsPullNew(ctx *itypes.Context, req *proto.NewsPullNewReq) (resp *proto.NewsPullNewResp, total int64, code itypes.BizCode) {
|
||||||
dos, total, err := m.newsDAO.QueryPullNew(&dao.NewsPullNewCondition{
|
dos, total, err := m.newsDAO.QueryPullNew(&dao.NewsPullNewCondition{
|
||||||
OrgIDs: req.OrgIDs,
|
OrgIDs: req.OrgIDs,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ import (
|
||||||
"github.com/civet148/sqlca/v2"
|
"github.com/civet148/sqlca/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 客户端带来的本地状态 (org_id, digest)
|
||||||
|
type OrgDigestPair struct {
|
||||||
|
OrgId int64
|
||||||
|
Digest string
|
||||||
|
}
|
||||||
|
|
||||||
type NewsAsyncCondition struct {
|
type NewsAsyncCondition struct {
|
||||||
Org_Id int64
|
Org_Id int64
|
||||||
Digest string
|
Digest string
|
||||||
|
|
@ -393,6 +399,52 @@ func (dao *NewsDAO) QueryAsync(cond *NewsAsyncCondition) (dos []*models.NewsDO,
|
||||||
return []*models.NewsDO{record}, 1, nil
|
return []*models.NewsDO{record}, 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 一次 SQL 抓取该批 org_id,对比 digest,返回需要下发的记录
|
||||||
|
func (dao *NewsDAO) QueryAsyncBatch(pairs []OrgDigestPair) (dos []*models.NewsDO, total int64, err error) {
|
||||||
|
|
||||||
|
if len(pairs) == 0 {
|
||||||
|
return []*models.NewsDO{}, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ① 提取 org_id 列表,同时建映射表 clientDigest[org_id] = digest
|
||||||
|
orgIDs := make([]int64, 0, len(pairs))
|
||||||
|
clientDigest := make(map[int64]string, len(pairs))
|
||||||
|
for _, p := range pairs {
|
||||||
|
orgIDs = append(orgIDs, p.OrgId)
|
||||||
|
clientDigest[p.OrgId] = p.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// ② 一次 SQL:SELECT * FROM news WHERE org_id IN ( … )
|
||||||
|
var rows []*models.NewsDO
|
||||||
|
// err 已在函数签名里声明
|
||||||
|
_, err = dao.db.
|
||||||
|
Model(&rows). // 告诉 sqlca 目标对象是 []*models.NewsDO
|
||||||
|
Table(models.TableNameNews).
|
||||||
|
Where("org_id IN ?", orgIDs).
|
||||||
|
Query() // 用 Query 而不是 Find
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ③ 在 Go 层比对 digest
|
||||||
|
for _, row := range rows {
|
||||||
|
cDigest := clientDigest[row.OrgId] // 客户端传来的 digest
|
||||||
|
|
||||||
|
// 直接从 map[string]interface{} 里取
|
||||||
|
var sDigest string
|
||||||
|
if d, ok := row.ExtraData["digest"].(string); ok {
|
||||||
|
sDigest = d
|
||||||
|
}
|
||||||
|
|
||||||
|
// cDigest 为空 或 与服务器端不同,都需要下发
|
||||||
|
if cDigest == "" || cDigest != sDigest {
|
||||||
|
dos = append(dos, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dos, int64(len(dos)), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dao *NewsDAO) QueryPullNew(cond *NewsPullNewCondition) (dos []*models.NewsDO, total int64, err error) {
|
func (dao *NewsDAO) QueryPullNew(cond *NewsPullNewCondition) (dos []*models.NewsDO, total int64, err error) {
|
||||||
e := dao.db.Model(&dos).
|
e := dao.db.Model(&dos).
|
||||||
Table(models.TableNameNews).
|
Table(models.TableNameNews).
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,10 @@ type NewsAsyncReq struct {
|
||||||
Digest string `json:"digest"`
|
Digest string `json:"digest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NewsAsyncBatchReq struct {
|
||||||
|
List []NewsAsyncReq `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
type NewsAsyncResp struct {
|
type NewsAsyncResp struct {
|
||||||
List []*models.NewsDO `json:"list"`
|
List []*models.NewsDO `json:"list"`
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +300,3 @@ type NewsPullNewReq struct {
|
||||||
type NewsPullNewResp struct {
|
type NewsPullNewResp struct {
|
||||||
List []*models.NewsDO `json:"list"`
|
List []*models.NewsDO `json:"list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewsAsyncBatchReq struct {
|
|
||||||
List []NewsAsyncReq `json:"list"`
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ const (
|
||||||
RouterSubPathNewsDraftDelete = "/draft/delete"
|
RouterSubPathNewsDraftDelete = "/draft/delete"
|
||||||
RouterSubPathNewsTag = "/tag"
|
RouterSubPathNewsTag = "/tag"
|
||||||
RouterSubPathNewsAsync = "/async"
|
RouterSubPathNewsAsync = "/async"
|
||||||
|
RouterSubPathNewsAsyncBatch = "/asyncbatch"
|
||||||
RouterSubPathNewsPullNew = "/pullnew"
|
RouterSubPathNewsPullNew = "/pullnew"
|
||||||
RouterSubPathQaList = "/list"
|
RouterSubPathQaList = "/list"
|
||||||
RouterSubPathQaAdd = "/add"
|
RouterSubPathQaAdd = "/add"
|
||||||
|
|
@ -51,6 +52,7 @@ func InitRouterGroupBiz(r *gin.Engine, handlers api.BizApi) {
|
||||||
|
|
||||||
groupNews.POST(RouterSubPathNewsList, handlers.NewsList)
|
groupNews.POST(RouterSubPathNewsList, handlers.NewsList)
|
||||||
groupNews.POST(RouterSubPathNewsAsync, handlers.NewsAsync)
|
groupNews.POST(RouterSubPathNewsAsync, handlers.NewsAsync)
|
||||||
|
groupNews.POST(RouterSubPathNewsAsyncBatch, handlers.NewsAsyncBatch)
|
||||||
groupNews.POST(RouterSubPathNewsPullNew, handlers.NewsPullNew)
|
groupNews.POST(RouterSubPathNewsPullNew, handlers.NewsPullNew)
|
||||||
groupNews.Use(middleware.JWT()) //use JWT token middleware
|
groupNews.Use(middleware.JWT()) //use JWT token middleware
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue