附件系统应该怎么设计比较好? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
Ketteiron
0.2D
0.03D
V2EX    Java

附件系统应该怎么设计比较好?

  •  
  •   Ketteiron Dec 15, 2021 4999 views
    This topic created in 1596 days ago, the information mentioned may be changed or developed.
    假如我有 20 张以上的业务表,每张表需要一个或多个附件字段,比如 xxx 信息表,有一个证明材料字段,有个验收材料字段,都是可以上传多个附件的。那我该怎么设计附件系统比较好?
    我的初步设想是,首先一个附件主表 file,一个附件从表 file_detail,一对多,从表存文件的路径和具体信息,每个业务表的附件字段存 file_id,这样就能关联起来。但是开发的时候又犯难了,上传的时候,我这么设计文件上传又没办法关联到业务表(上传和表单提交是分开的)。我想到文件上传之后,我把 file_id 返回给前端,前端提交表单再一起提交上来。但是这样又有新的问题,如果用户想要分次上传,比如两张图片,他先上传了 1 张,然后再传另 1 张,就会返回两个 file_id 过来。
    我看了公司之前有个项目也涉及到附件,他是这么做的,附件表每多一个业务表就加一个字段,20 张业务表就是 20 个 idxxx 、idxxx 这样的字段,上传的时候把自己的主键存到对应字段,不过这样没办法业务表多附件字段,除非 idxxx 字段加后缀接着细分,而且数据库一堆 null 有点蛋疼。
    各位有什么好的建议吗?
    32 replies    2021-12-16 14:07:57 +08:00
    thinkershare
        1
    thinkershare  
       Dec 15, 2021   1
    这个问题不是很容易解决吗? 文件上传只做上传, 不要做任何逻辑就好了, 逻辑交给业务接口处理, 文件只返回临时存储的地址, 后面的文件上的业务由业务接口处理
    Ayanokouji
        2
    Ayanokouji  
       Dec 15, 2021
    minio
    eason1874
        3
    eason1874  
       Dec 15, 2021
    用户要填表,进入表单,你就初始化表单,就是往数据库插入一条空信息,返回业务 form_id 给前端(要检查有没有历史空表单,有应该直接返回或者删除后再创建,防止用户每次进入都创建一个空表单)

    用户填表时,单独上传文件,你连着表单名、字段名一起上传,成功写入文件后就把 file_id 关联到 form_id ,返回状态时也返回字段名,根据字段名填充 file_id 到表单,提交时再验证一下
    Ketteiron
        4
    Ketteiron  
    OP
       Dec 15, 2021
    @eason1874 这样能实现,但是流程一下子复杂了好多
    aliveyang
        5
    aliveyang  
       Dec 15, 2021
    业务表中为什么要有附件字段?不是一对多么
    xuelu520
        6
    xuelu520  
       Dec 15, 2021   1
    首先你想复杂了。
    1 、附件上传是独立的,存附件表信息,返回附件表的 id 。
    2 、可以弄个通用关联表
    例如:
    业务类型 业务类型 ID 附件类型 附件 ID
    A 业务 1 A1 file_id
    B 业务 2 B1 file_id

    这样就不用动到业务表那边去
    gengchun
        7
    gengchun  
       Dec 15, 2021
    @Ayanokouji OP 也没有提他用的是 S3 ,也没有提对象存储,也没有提自己做的是分布式应用。这是怎么联想到的?
    nekoneko
        8
    nekoneko  
       Dec 15, 2021
    字段给个数组类型不行吗?
    Mac
        9
    Mac  
       Dec 15, 2021
    是表设计还是存储设计?

    表就是简单的关联子表呗,分类完全可以用字段来替代,只记录上传后的文件名和上传者 ID ,文件直接放到存储目录下。

    存储设计我现在是用 /UPLOAD/上传人 /业务号 /随机文件名.xxx 来做存储的
    cp19890714
        10
    cp19890714  
       Dec 15, 2021
    ## 一张表即可

    `id`
    `batch_no`
    `file_name`
    `file_type`
    `file_size`
    `url`
    `business_type`
    `expire_at`
    `active_flag`
    `del_flag`


    ## interface

    //打开新的 BatchNo, 返回 batchNo
    openBatchNo

    //添加文件到 BatchNo, 返回所有文件的执行结果
    upload(String batchNo, List<File> files));

    //可多次 commit. 每次 commit,激活本次提交的 file,其他 file 失活
    commitBatchNo(String batchNo, List<String> fileIds)

    get(String batchNo);

    delete(String batchNO)


    ## 说明
    * 1 个 batchNo 可以添加多个 file
    * file 可以无限次的追加到已有的 BatchNo 中.
    * 通过 active_flag 实现事务,只有 commit 后,file 才有效. 无效的 file 会被定时删除.
    * 业务表使用 batchNo 关联
    zoharSoul
        11
    zoharSoul  
       Dec 15, 2021   1
    业务表直接存附件(文件)地址不完事了.
    弄个附件表干啥?
    fpure
        12
    fpure  
       Dec 15, 2021
    建议直接存成一个 json 到数据库里
    Huelse
        13
    Huelse  
       Dec 15, 2021
    我觉得怎么设计都是家常便饭,像提交表单一样,重要的是文件放在那里,建议一开始就做好独立存储的准备
    815979670
        14
    815979670  
       Dec 15, 2021
    一个附件表即可,附件表存储 附件 id ,附件路径,附件的业务表,关联业务 id 信息。
    第一步,图片上次成功,写入附件路径,得到附件 id ,返回给客户端。
    第二步,业务执行保存操作,根据附件 id 附件业务表明和业务 id 写回附件表。

    第一步实现了通用上传附件,第二部实现了附件的信息补全。

    最后做一个定时任务,当附件表的附件上传超过一定时间(如 24 小时),但是没有回写 业务 id 的就是脏数据(上传了福附件但是没有保存的),定时清除数据和对应文件。
    rb6221
        15
    rb6221  
       Dec 15, 2021
    附件只需要一张表,file 表就不要了,业务表就包含了 file 的功能。
    为什么业务表和附件表不能一对多?
    hingbong
        16
    hingbong  
       Dec 15, 2021 via Android
    MySQL blob 直接存
    Valid
        17
    Valid  
       Dec 15, 2021
    minio 直接用 s3 的 sdk
    Ketteiron
        18
    Ketteiron  
    OP
       Dec 16, 2021
    @nekoneko 那样展示整页的表单数据就有点麻烦了,每行的多个数组分别做一次查询?还是一次性把所有的数组合并然后查出所有的附件,然后再多次遍历放到各自的业务列表里去?
    857681664
        19
    857681664  
       Dec 16, 2021 via Android
    把附件信息以 json 数组的形式存进附件表,同时用 id 和 type 字段与相对应的业务表进行关联
    nekoneko
        20
    nekoneko  
       Dec 16, 2021
    @zoharSoul #11 附件删除了怎么办
    nekoneko
        21
    nekoneko  
       Dec 16, 2021
    @dssxzuxc #18 对呀
    nekoneko
        22
    nekoneko  
       Dec 16, 2021
    @zoharSoul #11 附件可能有上传者,上传时间等等信息,也就没了
    zoharSoul
        23
    zoharSoul  
       Dec 16, 2021
    @nekoneko #22 全追加到业务表上就行
    zoharSoul
        24
    zoharSoul  
       Dec 16, 2021
    @nekoneko #20 删除了就把业务表上的附件相关字段清掉啊
    nekoneko
        25
    nekoneko  
       Dec 16, 2021
    @zoharSoul #24 业务表只存了个地址,是要字符串匹配着清吗
    #23 那意思就是业务表存 json 呗
    nekoneko
        26
    nekoneko  
       Dec 16, 2021
    @zoharSoul #24 你可能看漏了,楼主说了每张表有多个附件字段,每个字段可以有多个附件,我感觉你理解成每个字段只有一个附件了
    zoharSoul
        27
    zoharSoul  
       Dec 16, 2021
    @nekoneko #26 嗯, 我的意思就是业务表存 json
    2i2Re2PLMaDnghL
        28
    2i2Re2PLMaDnghL  
       Dec 16, 2021
    『关系型数据库里的数组需要转置一下存成多条记录』
    (这是 RDBMS 还没几个数组类型的时候说的)

    一对多就是把关系倒转,是附件表里带业务表的 ID 。
    neptuno
        29
    neptuno  
       Dec 16, 2021 via iPhone
    上传文件是单独的功能,表单跟附件表可以一对多
    Ketteiron
        30
    Ketteiron  
    OP
       Dec 16, 2021
    @815979670 这个可行,附件表里加上业务 id 、业务表名、业务字段名 3 个字段,不过插入字段名只能用硬编码,查询 sql 也是用硬编码拼接,而这个字段名对于业务 entity 是多余的,因为业务表不存附件表信息,那这个字段名我只能放 vo 类里面,感觉后期维护容易写出 bug
    815979670
        31
    815979670  
       Dec 16, 2021
    @dssxzuxc 如果业务表不存附件信息的话,附件表必须存业务表名+业务 id 啊,不然怎么确定这个附件是属于那个业务表的?
    至于硬编码问题,如果你们对查询接口做了同意封装,应该可以通过传参 或者取表名的方式解决。
    unco020511
        32
    unco020511  
       Dec 16, 2021
    文件上传做成与业务无关的服务,后期管理和切换都方便.业务这边直接存文件 url 就行,有个表来关联 file 和业务 id,你想怎么对应都可以
    About     Help     Advertise     Blog     API     FAQ     Solana     1420 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 74ms UTC 17:06 PVG 01:06 LAX 10:06 JFK 13:06
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86